From 20da8caf4b91cfe68f1478e36825774cf3800c92 Mon Sep 17 00:00:00 2001 From: Adrian Conlon Date: Tue, 24 Feb 2026 20:06:37 +0000 Subject: [PATCH] Update simdjson --- M6502/HarteTest_6502/simdjson/simdjson.cpp | 92995 +++--- M6502/HarteTest_6502/simdjson/simdjson.h | 274471 +++++++++++------- 2 files changed, 220586 insertions(+), 146880 deletions(-) diff --git a/M6502/HarteTest_6502/simdjson/simdjson.cpp b/M6502/HarteTest_6502/simdjson/simdjson.cpp index 515a3e6..0374b89 100644 --- a/M6502/HarteTest_6502/simdjson/simdjson.cpp +++ b/M6502/HarteTest_6502/simdjson/simdjson.cpp @@ -1,4 +1,4 @@ -/* auto-generated on 2024-08-26 09:37:03 -0400. Do not edit! */ +/* auto-generated on 2026-02-20 16:16:37 -0500. version 4.3.1 Do not edit! */ /* including simdjson.cpp: */ /* begin file simdjson.cpp */ #define SIMDJSON_SRC_SIMDJSON_CPP @@ -17,8 +17,8 @@ #ifndef SIMDJSON_BASE_H #define SIMDJSON_BASE_H - /* including simdjson/common_defs.h: #include "simdjson/common_defs.h" */ - /* begin file simdjson/common_defs.h */ +/* 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 @@ -40,6 +40,11 @@ #endif #endif +// C++ 26 +#if !defined(SIMDJSON_CPLUSPLUS26) && (SIMDJSON_CPLUSPLUS >= 202402L) // update when the standard is finalized +#define SIMDJSON_CPLUSPLUS26 1 +#endif + // C++ 23 #if !defined(SIMDJSON_CPLUSPLUS23) && (SIMDJSON_CPLUSPLUS >= 202302L) #define SIMDJSON_CPLUSPLUS23 1 @@ -77,6 +82,71 @@ #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() +#include +#endif +#endif + +// The current specification is unclear on how we detect +// static reflection, both __cpp_lib_reflection and +// __cpp_impl_reflection are proposed in the draft specification. +// For now, we disable static reflect by default. It must be +// specified at compiler time. +#ifndef SIMDJSON_STATIC_REFLECTION +#define SIMDJSON_STATIC_REFLECTION 0 // disabled by default. +#endif + +#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 +#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 +#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 + + +#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) + #endif // SIMDJSON_COMPILER_CHECK_H /* end file simdjson/compiler_check.h */ /* including simdjson/portability.h: #include "simdjson/portability.h" */ @@ -89,11 +159,15 @@ #include #include #include +#include #ifndef _WIN32 // strcasecmp, strncasecmp #include #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; @@ -110,10 +184,10 @@ using std::size_t; * */ #ifdef __clang__ - // clang under visual studio +// clang under visual studio #define SIMDJSON_CLANG_VISUAL_STUDIO 1 #else - // just regular visual studio (best guess) +// just regular visual studio (best guess) #define SIMDJSON_REGULAR_VISUAL_STUDIO 1 #endif // __clang__ #endif // _MSC_VER @@ -124,9 +198,30 @@ using std::size_t; #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 +#endif #elif defined(__PPC64__) || defined(_M_PPC64) +#define SIMDJSON_IS_PPC64 1 #if defined(__ALTIVEC__) #define SIMDJSON_IS_PPC64_VMX 1 #endif // defined(__ALTIVEC__) @@ -180,7 +275,7 @@ using std::size_t; // // We are going to use runtime dispatch. -#if SIMDJSON_IS_X86_64 +#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 @@ -197,7 +292,7 @@ using std::size_t; #define SIMDJSON_UNTARGET_REGION _Pragma("GCC pop_options") #endif // clang then gcc -#endif // x86 +#endif // defined(SIMDJSON_IS_X86_64) || defined(SIMDJSON_IS_LSX) // Default target region macros don't do anything. #ifndef SIMDJSON_TARGET_REGION @@ -266,9 +361,11 @@ using std::size_t; #define simdjson_strncasecmp strncasecmp #endif -#if defined(NDEBUG) || defined(__OPTIMIZE__) || (defined(_MSC_VER) && !defined(_DEBUG)) +#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) @@ -277,35 +374,74 @@ using std::size_t; #define SIMDJSON_ASSUME(COND) do { if (!(COND)) __builtin_unreachable(); } while (0) #endif -#else // defined(NDEBUG) || defined(__OPTIMIZE__) || (defined(_MSC_VER) && !defined(_DEBUG)) +#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 +#elif defined(sun) || defined(__sun) +#include +#elif defined(__MVS__) +#include +#else +#ifdef __has_include +#if __has_include() +#include +#endif //__has_include() +#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 + + #endif // SIMDJSON_PORTABILITY_H /* end file simdjson/portability.h */ namespace simdjson { - namespace internal { - /** - * @private - * Our own implementation of the C++17 to_chars function. - * Defined in src/to_chars - */ - 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; - } +namespace internal { +/** + * @private + * Our own implementation of the C++17 to_chars function. + * Defined in src/to_chars + */ +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; +} #ifndef SIMDJSON_EXCEPTIONS -#if __cpp_exceptions +#if defined(__cpp_exceptions) || defined(_CPPUNWIND) #define SIMDJSON_EXCEPTIONS 1 #else #define SIMDJSON_EXCEPTIONS 0 @@ -316,13 +452,13 @@ 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); + #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) + #define SIMDJSON_BEGIN_DEBUG_BLOCK(name) + #define SIMDJSON_END_DEBUG_BLOCK(name) + #define SIMDJSON_DEBUG_BLOCK(name, block) #endif // Align to N-byte boundary @@ -333,67 +469,67 @@ namespace simdjson { #if SIMDJSON_REGULAR_VISUAL_STUDIO // We could use [[deprecated]] but it requires C++14 -#define simdjson_deprecated __declspec(deprecated) + #define simdjson_deprecated __declspec(deprecated) -#define simdjson_really_inline __forceinline -#define simdjson_never_inline __declspec(noinline) + #define simdjson_really_inline __forceinline + #define simdjson_never_inline __declspec(noinline) -#define simdjson_unused -#define simdjson_warn_unused + #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 + #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() -#include -#define SIMDJSON_DISABLE_UNDESIRED_WARNINGS SIMDJSON_DISABLE_VS_WARNING(ALL_CPPCORECHECK_WARNINGS) -#endif -#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() + #include + #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 + #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_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 + #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_deprecated __attribute__((deprecated)) -#define simdjson_really_inline inline __attribute__((always_inline)) -#define simdjson_never_inline inline __attribute__((noinline)) + #define simdjson_really_inline inline __attribute__((always_inline)) + #define simdjson_never_inline inline __attribute__((noinline)) -#define simdjson_unused __attribute__((unused)) -#define simdjson_warn_unused __attribute__((warn_unused_result)) + #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 + #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 \ + #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) \ @@ -405,8 +541,8 @@ namespace simdjson { 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 \ + #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) \ @@ -420,22 +556,22 @@ namespace simdjson { SIMDJSON_DISABLE_GCC_WARNING(-Wunused-variable) \ SIMDJSON_DISABLE_GCC_WARNING(-Wmaybe-uninitialized) \ SIMDJSON_DISABLE_GCC_WARNING(-Wformat-security) -#endif // __clang__ + #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_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 \ + #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 + #define SIMDJSON_POP_DISABLE_UNUSED_WARNINGS SIMDJSON_POP_DISABLE_WARNINGS @@ -447,10 +583,10 @@ namespace simdjson { // 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 + #define simdjson_inline inline #else // Force inlining for most simdjson functions. -#define simdjson_inline simdjson_really_inline + #define simdjson_inline simdjson_really_inline #endif #if SIMDJSON_VISUAL_STUDIO @@ -469,31 +605,20 @@ namespace simdjson { * * 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) + #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 - // We assume by default static linkage -#define SIMDJSON_DLLIMPORTEXPORT -#endif - -/** - * Workaround for the vcpkg package manager. Only vcpkg should - * ever touch the next line. The SIMDJSON_USING_LIBRARY macro is otherwise unused. - */ -#if SIMDJSON_USING_LIBRARY -#define SIMDJSON_DLLIMPORTEXPORT __declspec(dllimport) -#endif - /** - * End of workaround for the vcpkg package manager. - */ -#else -#define SIMDJSON_DLLIMPORTEXPORT + #define SIMDJSON_DLLIMPORTEXPORT #endif // C++17 requires string_view. @@ -508,12 +633,14 @@ namespace simdjson { // even if we do not have C++17 support. #ifdef __cpp_lib_string_view #define SIMDJSON_HAS_STRING_VIEW +#include #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) @@ -529,6 +656,7 @@ namespace simdjson { #endif // __has_include () #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. // @@ -549,7 +677,6 @@ SIMDJSON_PUSH_DISABLE_ALL_WARNINGS // 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) -#pragma once #ifndef NONSTD_SV_LITE_H_INCLUDED #define NONSTD_SV_LITE_H_INCLUDED @@ -681,56 +808,55 @@ SIMDJSON_PUSH_DISABLE_ALL_WARNINGS namespace nonstd { - template< class CharT, class Traits, class Allocator = std::allocator > - std::basic_string - to_string(std::basic_string_view v, Allocator const& a = Allocator()) - { - return std::basic_string(v.begin(), v.end(), a); - } +template< class CharT, class Traits, class Allocator = std::allocator > +std::basic_string +to_string( std::basic_string_view v, Allocator const & a = Allocator() ) +{ + return std::basic_string( v.begin(), v.end(), a ); +} - template< class CharT, class Traits, class Allocator > - std::basic_string_view - to_string_view(std::basic_string const& s) - { - return std::basic_string_view(s.data(), s.size()); - } +template< class CharT, class Traits, class Allocator > +std::basic_string_view +to_string_view( std::basic_string const & s ) +{ + return std::basic_string_view( s.data(), s.size() ); +} - // Literal operators sv and _sv: +// Literal operators sv and _sv: #if nssv_CONFIG_STD_SV_OPERATOR - using namespace std::literals::string_view_literals; +using namespace std::literals::string_view_literals; #endif #if nssv_CONFIG_USR_SV_OPERATOR - inline namespace literals { - inline namespace string_view_literals { +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::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::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::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 }; - } +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 +}} // namespace literals::string_view_literals #endif // nssv_CONFIG_USR_SV_OPERATOR @@ -740,22 +866,22 @@ namespace nonstd { namespace nonstd { - using std::string_view; - using std::wstring_view; - using std::u16string_view; - using std::u32string_view; - using std::basic_string_view; +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 +// 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==; +using std::operator!=; +using std::operator<; +using std::operator<=; +using std::operator>; +using std::operator>=; - using std::operator<<; +using std::operator<<; } // namespace nonstd @@ -953,9 +1079,9 @@ namespace nonstd { #endif #if nssv_HAVE_NODISCARD -# define nssv_nodiscard [[nodiscard]] +# define nssv_nodiscard simdjson_warn_unused #else -# define nssv_nodiscard /*[[nodiscard]]*/ +# define nssv_nodiscard /*simdjson_warn_unused*/ #endif // Additional includes: @@ -1016,721 +1142,708 @@ namespace nonstd { // 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_MSVC_WARNINGS( 4455 26481 26472 ) //nssv_DISABLE_CLANG_WARNINGS( "-Wuser-defined-literals" ) //nssv_DISABLE_GNUC_WARNINGS( -Wliteral-suffix ) -namespace nonstd { - namespace sv_lite { +namespace nonstd { namespace sv_lite { - // - // basic_string_view declaration: - // +// +// basic_string_view declaration: +// - template - < - class CharT, - class Traits = std::char_traits - > - class basic_string_view; +template +< + class CharT, + class Traits = std::char_traits +> +class basic_string_view; - namespace detail { +namespace detail { - // support constexpr comparison in C++14; - // for C++17 and later, use provided traits: +// 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; - } +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: +// 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); - } +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: +// 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); - } +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: +// 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); - } +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: +// 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; - } +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: +// gcc, clang provide __OPTIMIZE__ +// Expect tail call optimization to make search() non-recursive: - template< class CharT, class Traits = std::char_traits > - constexpr const CharT* search(basic_string_view haystack, basic_string_view needle) - { - return haystack.starts_with(needle) ? haystack.begin() : - haystack.empty() ? haystack.end() : search(haystack.substr(1), needle); - } +template< class CharT, class Traits = std::char_traits > +constexpr const CharT* search( basic_string_view haystack, basic_string_view needle ) +{ + return haystack.starts_with( needle ) ? haystack.begin() : + haystack.empty() ? haystack.end() : search( haystack.substr(1), needle ); +} #else // OPTIMIZE - // non-recursive: +// non-recursive: #if nssv_CONFIG_CONSTEXPR11_STD_SEARCH - template< class CharT, class Traits = std::char_traits > - constexpr const CharT* search(basic_string_view haystack, basic_string_view needle) - { - return std::search(haystack.begin(), haystack.end(), needle.begin(), needle.end()); - } +template< class CharT, class Traits = std::char_traits > +constexpr const CharT* search( basic_string_view haystack, basic_string_view 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 > - nssv_constexpr14 const CharT* search(basic_string_view haystack, basic_string_view needle) - { - while (needle.size() <= haystack.size()) - { - if (haystack.starts_with(needle)) - { - return haystack.cbegin(); - } - haystack = basic_string_view{ haystack.begin() + 1, haystack.size() - 1U }; - } - return haystack.cend(); - } +template< class CharT, class Traits = std::char_traits > +nssv_constexpr14 const CharT* search( basic_string_view haystack, basic_string_view needle ) +{ + while ( needle.size() <= haystack.size() ) + { + if ( haystack.starts_with(needle) ) + { + return haystack.cbegin(); + } + haystack = basic_string_view{ 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 +} // namespace detail - // - // basic_string_view: - // +// +// basic_string_view: +// - template - < - class CharT, - class Traits /* = std::char_traits */ - > - class basic_string_view - { - public: - // Member types: +template +< + class CharT, + class Traits /* = std::char_traits */ +> +class basic_string_view +{ +public: + // Member types: - typedef Traits traits_type; - typedef CharT value_type; + 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 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 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; + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; - // 24.4.2.1 Construction and assignment: + // 24.4.2.1 Construction and assignment: - nssv_constexpr basic_string_view() nssv_noexcept - : data_(nssv_nullptr) - , size_(0) - {} + 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; + 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_) - {} + 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, 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) + nssv_constexpr basic_string_view( CharT const * s) nssv_noexcept // non-standard noexcept + : data_( s ) #if nssv_CPP17_OR_GREATER - , size_(Traits::length(s)) + , size_( Traits::length(s) ) #elif nssv_CPP11_OR_GREATER - , size_(detail::length(s)) + , size_( detail::length(s) ) #else - , size_(Traits::length(s)) + , 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; + nssv_constexpr basic_string_view( std::nullptr_t ) nssv_noexcept = delete; # else - private: nssv_constexpr basic_string_view(std::nullptr_t) nssv_noexcept; public: + private: nssv_constexpr basic_string_view( std::nullptr_t ) nssv_noexcept; public: # endif #endif - // Assignment: + // Assignment: #if nssv_CPP11_OR_GREATER - nssv_constexpr14 basic_string_view& operator=(basic_string_view const& other) nssv_noexcept = default; + 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; - } + 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: + // 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 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_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 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(); } + 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: + // 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)(); } + 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_; - } + // since C++20 + nssv_nodiscard nssv_constexpr bool empty() const nssv_noexcept + { + return 0 == size_; + } - // 24.4.2.4 Element access: + // 24.4.2.4 Element access: - nssv_constexpr const_reference operator[](size_type pos) const - { - return data_at(pos); - } + nssv_constexpr const_reference operator[]( size_type pos ) const + { + return data_at( pos ); + } - nssv_constexpr14 const_reference at(size_type pos) const - { + nssv_constexpr14 const_reference at( size_type pos ) const + { #if nssv_CONFIG_NO_EXCEPTIONS - assert(pos < size()); + assert( pos < size() ); #else - if (pos >= size()) - { - throw std::out_of_range("nonstd::string_view::at()"); - } + if ( pos >= size() ) + { + throw std::out_of_range("nonstd::string_view::at()"); + } #endif - return data_at(pos); - } + 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_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_; } + nssv_constexpr const_pointer data() const nssv_noexcept { return data_; } - // 24.4.2.5 Modifiers: + // 24.4.2.5 Modifiers: - nssv_constexpr14 void remove_prefix(size_type n) - { - assert(n <= size()); - data_ += n; - size_ -= n; - } + 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 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; - } + 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: + // 24.4.2.6 String operations: - size_type copy(CharT* dest, size_type n, size_type pos = 0) const - { + size_type copy( CharT * dest, size_type n, size_type pos = 0 ) const + { #if nssv_CONFIG_NO_EXCEPTIONS - assert(pos <= size()); + assert( pos <= size() ); #else - if (pos > size()) - { - throw std::out_of_range("nonstd::string_view::copy()"); - } + if ( pos > size() ) + { + throw std::out_of_range("nonstd::string_view::copy()"); + } #endif - const size_type rlen = (std::min)(n, size() - pos); + const size_type rlen = (std::min)( n, size() - pos ); - (void)Traits::copy(dest, data() + pos, rlen); + (void) Traits::copy( dest, data() + pos, rlen ); - return rlen; - } + return rlen; + } - nssv_constexpr14 basic_string_view substr(size_type pos = 0, size_type n = npos) const - { + nssv_constexpr14 basic_string_view substr( size_type pos = 0, size_type n = npos ) const + { #if nssv_CONFIG_NO_EXCEPTIONS - assert(pos <= size()); + assert( pos <= size() ); #else - if (pos > size()) - { - throw std::out_of_range("nonstd::string_view::substr()"); - } + if ( pos > size() ) + { + throw std::out_of_range("nonstd::string_view::substr()"); + } #endif - return basic_string_view(data() + pos, (std::min)(n, size() - pos)); - } + return basic_string_view( data() + pos, (std::min)( n, size() - pos ) ); + } - // compare(), 6x: + // compare(), 6x: - nssv_constexpr14 int compare(basic_string_view other) const nssv_noexcept // (1) - { + 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()))) + 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()))) + if ( const int result = detail::compare( data(), other.data(), (std::min)( size(), other.size() ) ) ) #endif - { - return result; - } + { + return result; + } - return size() == other.size() ? 0 : size() < other.size() ? -1 : 1; - } + 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 ) 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( 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( 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 ) 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)); - } + 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: + // 24.4.2.7 Searching: - // starts_with(), 3x, since C++20: + // 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( 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 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)); - } + nssv_constexpr bool starts_with( CharT const * s ) const // (3) + { + return starts_with( basic_string_view( s ) ); + } - // ends_with(), 3x, since C++20: + // 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( 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 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)); - } + nssv_constexpr bool ends_with( CharT const * s ) const // (3) + { + return ends_with( basic_string_view( s ) ); + } - // find(), 4x: + // 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( + 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) + detail::search( substr(pos), v ) #else - std::search(cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq) + 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 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, 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); - } + nssv_constexpr size_type find( CharT const * s, size_type pos = 0 ) const // (4) + { + return find( basic_string_view( s ), pos ); + } - // rfind(), 4x: + // 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; - } + 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); - } + 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); + 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; - } + 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 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, 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); - } + 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: + // 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( 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 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, 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); - } + 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: + // 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( 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 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, 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); - } + 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: + // 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( 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 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, 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); - } + 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: + // 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( 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 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, 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); - } + 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: + // Constants: #if nssv_CPP17_OR_GREATER - static nssv_constexpr size_type npos = size_type(-1); + static nssv_constexpr size_type npos = size_type(-1); #elif nssv_CPP11_OR_GREATER - enum : size_type { npos = size_type(-1) }; + enum : size_type { npos = size_type(-1) }; #else - enum { npos = size_type(-1) }; + enum { npos = size_type(-1) }; #endif - private: - struct not_in_view - { - const basic_string_view v; +private: + struct not_in_view + { + const basic_string_view v; - nssv_constexpr explicit not_in_view(basic_string_view v_) : v(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 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_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 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 - { + nssv_constexpr const_reference data_at( size_type pos ) const + { #if nssv_BETWEEN( nssv_COMPILER_GNUC_VERSION, 1, 500 ) - return data_[pos]; + return data_[pos]; #else - return assert(pos < size()), data_[pos]; + return assert( pos < size() ), data_[pos]; #endif - } + } - private: - const_pointer data_; - size_type size_; +private: + const_pointer data_; + size_type size_; - public: +public: #if nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS - template< class Allocator > - basic_string_view(std::basic_string const& s) nssv_noexcept - : data_(s.data()) - , size_(s.size()) - {} + template< class Allocator > + basic_string_view( std::basic_string const & s ) nssv_noexcept + : data_( s.data() ) + , size_( s.size() ) + {} #if nssv_HAVE_EXPLICIT_CONVERSION - template< class Allocator > - explicit operator std::basic_string() const - { - return to_string(Allocator()); - } + template< class Allocator > + explicit operator std::basic_string() const + { + return to_string( Allocator() ); + } #endif // nssv_HAVE_EXPLICIT_CONVERSION #if nssv_CPP11_OR_GREATER - template< class Allocator = std::allocator > - std::basic_string - to_string(Allocator const& a = Allocator()) const - { - return std::basic_string(begin(), end(), a); - } + template< class Allocator = std::allocator > + std::basic_string + to_string( Allocator const & a = Allocator() ) const + { + return std::basic_string( begin(), end(), a ); + } #else - std::basic_string - to_string() const - { - return std::basic_string(begin(), end()); - } + std::basic_string + to_string() const + { + return std::basic_string( begin(), end() ); + } - template< class Allocator > - std::basic_string - to_string(Allocator const& a) const - { - return std::basic_string(begin(), end(), a); - } + template< class Allocator > + std::basic_string + to_string( Allocator const & a ) const + { + return std::basic_string( begin(), end(), a ); + } #endif // nssv_CPP11_OR_GREATER #endif // nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS - }; +}; - // - // Non-member functions: - // +// +// Non-member functions: +// - // 24.4.3 Non-member comparison functions: - // lexicographically compare two string views (function template): +// 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 lhs, - basic_string_view rhs) nssv_noexcept - { - return lhs.size() == rhs.size() && lhs.compare(rhs) == 0; - } +template< class CharT, class Traits > +nssv_constexpr bool operator== ( + basic_string_view lhs, + basic_string_view rhs ) nssv_noexcept +{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; } - template< class CharT, class Traits > - nssv_constexpr bool operator!= ( - basic_string_view lhs, - basic_string_view rhs) nssv_noexcept - { - return !(lhs == rhs); - } +template< class CharT, class Traits > +nssv_constexpr bool operator!= ( + basic_string_view lhs, + basic_string_view rhs ) nssv_noexcept +{ return !( lhs == rhs ); } - template< class CharT, class Traits > - nssv_constexpr bool operator< ( - basic_string_view lhs, - basic_string_view rhs) nssv_noexcept - { - return lhs.compare(rhs) < 0; - } +template< class CharT, class Traits > +nssv_constexpr bool operator< ( + basic_string_view lhs, + basic_string_view rhs ) nssv_noexcept +{ return lhs.compare( rhs ) < 0; } - template< class CharT, class Traits > - nssv_constexpr bool operator<= ( - basic_string_view lhs, - basic_string_view rhs) nssv_noexcept - { - return lhs.compare(rhs) <= 0; - } +template< class CharT, class Traits > +nssv_constexpr bool operator<= ( + basic_string_view lhs, + basic_string_view rhs ) nssv_noexcept +{ return lhs.compare( rhs ) <= 0; } - template< class CharT, class Traits > - nssv_constexpr bool operator> ( - basic_string_view lhs, - basic_string_view rhs) nssv_noexcept - { - return lhs.compare(rhs) > 0; - } +template< class CharT, class Traits > +nssv_constexpr bool operator> ( + basic_string_view lhs, + basic_string_view rhs ) nssv_noexcept +{ return lhs.compare( rhs ) > 0; } - template< class CharT, class Traits > - nssv_constexpr bool operator>= ( - basic_string_view lhs, - basic_string_view rhs) nssv_noexcept - { - return lhs.compare(rhs) >= 0; - } +template< class CharT, class Traits > +nssv_constexpr bool operator>= ( + basic_string_view lhs, + basic_string_view rhs ) nssv_noexcept +{ return lhs.compare( rhs ) >= 0; } - // Let S be basic_string_view, 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. +// Let S be basic_string_view, 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 ) @@ -1738,207 +1851,159 @@ namespace nonstd { // == - template< class CharT, class Traits> - nssv_constexpr bool operator==( - basic_string_view 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==( + basic_string_view 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 rhs) nssv_noexcept - { - return detail::length(lhs) == rhs.size() && rhs.compare(lhs) == 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator==( + CharT const * lhs, + basic_string_view 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 lhs, - std::basic_string rhs) nssv_noexcept - { - return lhs.size() == rhs.size() && lhs.compare(rhs) == 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator==( + basic_string_view lhs, + std::basic_string rhs ) nssv_noexcept +{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; } - template< class CharT, class Traits> - nssv_constexpr bool operator==( - std::basic_string rhs, - basic_string_view lhs) nssv_noexcept - { - return lhs.size() == rhs.size() && lhs.compare(rhs) == 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator==( + std::basic_string rhs, + basic_string_view lhs ) nssv_noexcept +{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; } - // != +// != - template< class CharT, class Traits> - nssv_constexpr bool operator!=( - basic_string_view lhs, - CharT const* rhs) nssv_noexcept - { - return !(lhs == rhs); - } +template< class CharT, class Traits> +nssv_constexpr bool operator!=( + basic_string_view lhs, + CharT const * rhs ) nssv_noexcept +{ return !( lhs == rhs ); } - template< class CharT, class Traits> - nssv_constexpr bool operator!=( - CharT const* lhs, - basic_string_view rhs) nssv_noexcept - { - return !(lhs == rhs); - } +template< class CharT, class Traits> +nssv_constexpr bool operator!=( + CharT const * lhs, + basic_string_view rhs ) nssv_noexcept +{ return !( lhs == rhs ); } - template< class CharT, class Traits> - nssv_constexpr bool operator!=( - basic_string_view lhs, - std::basic_string rhs) nssv_noexcept - { - return !(lhs == rhs); - } +template< class CharT, class Traits> +nssv_constexpr bool operator!=( + basic_string_view lhs, + std::basic_string rhs ) nssv_noexcept +{ return !( lhs == rhs ); } - template< class CharT, class Traits> - nssv_constexpr bool operator!=( - std::basic_string rhs, - basic_string_view lhs) nssv_noexcept - { - return !(lhs == rhs); - } +template< class CharT, class Traits> +nssv_constexpr bool operator!=( + std::basic_string rhs, + basic_string_view lhs ) nssv_noexcept +{ return !( lhs == rhs ); } - // < +// < - template< class CharT, class Traits> - nssv_constexpr bool operator<( - basic_string_view lhs, - CharT const* rhs) nssv_noexcept - { - return lhs.compare(rhs) < 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator<( + basic_string_view 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 rhs) nssv_noexcept - { - return rhs.compare(lhs) > 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator<( + CharT const * lhs, + basic_string_view rhs ) nssv_noexcept +{ return rhs.compare( lhs ) > 0; } - template< class CharT, class Traits> - nssv_constexpr bool operator<( - basic_string_view lhs, - std::basic_string rhs) nssv_noexcept - { - return lhs.compare(rhs) < 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator<( + basic_string_view lhs, + std::basic_string rhs ) nssv_noexcept +{ return lhs.compare( rhs ) < 0; } - template< class CharT, class Traits> - nssv_constexpr bool operator<( - std::basic_string rhs, - basic_string_view lhs) nssv_noexcept - { - return rhs.compare(lhs) > 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator<( + std::basic_string rhs, + basic_string_view lhs ) nssv_noexcept +{ return rhs.compare( lhs ) > 0; } - // <= +// <= - template< class CharT, class Traits> - nssv_constexpr bool operator<=( - basic_string_view lhs, - CharT const* rhs) nssv_noexcept - { - return lhs.compare(rhs) <= 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator<=( + basic_string_view 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 rhs) nssv_noexcept - { - return rhs.compare(lhs) >= 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator<=( + CharT const * lhs, + basic_string_view rhs ) nssv_noexcept +{ return rhs.compare( lhs ) >= 0; } - template< class CharT, class Traits> - nssv_constexpr bool operator<=( - basic_string_view lhs, - std::basic_string rhs) nssv_noexcept - { - return lhs.compare(rhs) <= 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator<=( + basic_string_view lhs, + std::basic_string rhs ) nssv_noexcept +{ return lhs.compare( rhs ) <= 0; } - template< class CharT, class Traits> - nssv_constexpr bool operator<=( - std::basic_string rhs, - basic_string_view lhs) nssv_noexcept - { - return rhs.compare(lhs) >= 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator<=( + std::basic_string rhs, + basic_string_view lhs ) nssv_noexcept +{ return rhs.compare( lhs ) >= 0; } - // > +// > - template< class CharT, class Traits> - nssv_constexpr bool operator>( - basic_string_view lhs, - CharT const* rhs) nssv_noexcept - { - return lhs.compare(rhs) > 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator>( + basic_string_view 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 rhs) nssv_noexcept - { - return rhs.compare(lhs) < 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator>( + CharT const * lhs, + basic_string_view rhs ) nssv_noexcept +{ return rhs.compare( lhs ) < 0; } - template< class CharT, class Traits> - nssv_constexpr bool operator>( - basic_string_view lhs, - std::basic_string rhs) nssv_noexcept - { - return lhs.compare(rhs) > 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator>( + basic_string_view lhs, + std::basic_string rhs ) nssv_noexcept +{ return lhs.compare( rhs ) > 0; } - template< class CharT, class Traits> - nssv_constexpr bool operator>( - std::basic_string rhs, - basic_string_view lhs) nssv_noexcept - { - return rhs.compare(lhs) < 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator>( + std::basic_string rhs, + basic_string_view lhs ) nssv_noexcept +{ return rhs.compare( lhs ) < 0; } - // >= +// >= - template< class CharT, class Traits> - nssv_constexpr bool operator>=( - basic_string_view lhs, - CharT const* rhs) nssv_noexcept - { - return lhs.compare(rhs) >= 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator>=( + basic_string_view 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 rhs) nssv_noexcept - { - return rhs.compare(lhs) <= 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator>=( + CharT const * lhs, + basic_string_view rhs ) nssv_noexcept +{ return rhs.compare( lhs ) <= 0; } - template< class CharT, class Traits> - nssv_constexpr bool operator>=( - basic_string_view lhs, - std::basic_string rhs) nssv_noexcept - { - return lhs.compare(rhs) >= 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator>=( + basic_string_view lhs, + std::basic_string rhs ) nssv_noexcept +{ return lhs.compare( rhs ) >= 0; } - template< class CharT, class Traits> - nssv_constexpr bool operator>=( - std::basic_string rhs, - basic_string_view lhs) nssv_noexcept - { - return rhs.compare(lhs) <= 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator>=( + std::basic_string rhs, + basic_string_view lhs ) nssv_noexcept +{ return rhs.compare( lhs ) <= 0; } #else // newer compilers: @@ -1952,183 +2017,158 @@ namespace nonstd { // == - template< class CharT, class Traits nssv_MSVC_ORDER(1) > - nssv_constexpr bool operator==( - basic_string_view 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(1) > +nssv_constexpr bool operator==( + basic_string_view 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 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 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(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(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(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(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(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(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(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(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(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(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: +// 24.4.4 Inserters and extractors: #if ! nssv_CONFIG_NO_STREAM_INSERTION - namespace detail { +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 > +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); +template< class Stream, class View > +Stream & write_to_stream( Stream & os, View const & sv ) +{ + typename Stream::sentry sentry( os ); - if (!sentry) - return os; + if ( !sentry ) + return os; - const std::streamsize length = static_cast(sv.length()); + const std::streamsize length = static_cast( 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; + // 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); + if ( left_pad ) + write_padding( os, os.width() - length ); - // Write span characters: - os.rdbuf()->sputn(sv.begin(), length); + // Write span characters: + os.rdbuf()->sputn( sv.begin(), length ); - if (pad && !left_pad) - write_padding(os, os.width() - length); + if ( pad && !left_pad ) + write_padding( os, os.width() - length ); - // Reset output stream width: - os.width(0); + // Reset output stream width: + os.width( 0 ); - return os; - } + return os; +} - } // namespace detail +} // namespace detail - template< class CharT, class Traits > - std::basic_ostream& - operator<<( - std::basic_ostream& os, - basic_string_view sv) - { - return detail::write_to_stream(os, sv); - } +template< class CharT, class Traits > +std::basic_ostream & +operator<<( + std::basic_ostream& os, + basic_string_view sv ) +{ + return detail::write_to_stream( os, sv ); +} #endif // nssv_CONFIG_NO_STREAM_INSERTION - // Several typedefs for common character types are provided: +// Several typedefs for common character types are provided: - typedef basic_string_view string_view; - typedef basic_string_view wstring_view; +typedef basic_string_view string_view; +typedef basic_string_view wstring_view; #if nssv_HAVE_WCHAR16_T - typedef basic_string_view u16string_view; - typedef basic_string_view u32string_view; +typedef basic_string_view u16string_view; +typedef basic_string_view u32string_view; #endif - } -} // namespace nonstd::sv_lite +}} // namespace nonstd::sv_lite // // 24.4.6 Suffix for basic_string_view literals: @@ -2137,60 +2177,58 @@ namespace nonstd { #if nssv_HAVE_USER_DEFINED_LITERALS namespace nonstd { - nssv_inline_ns namespace literals { - nssv_inline_ns namespace string_view_literals { +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::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::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::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 }; - } +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::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::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::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 }; - } +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 +}}} // namespace nonstd::literals::string_view_literals #endif @@ -2201,46 +2239,45 @@ namespace nonstd { #if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS namespace nonstd { - namespace sv_lite { +namespace sv_lite { - // Exclude MSVC 14 (19.00): it yields ambiguous to_string(): +// 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 > - std::basic_string - to_string(basic_string_view v, Allocator const& a = Allocator()) - { - return std::basic_string(v.begin(), v.end(), a); - } +template< class CharT, class Traits, class Allocator = std::allocator > +std::basic_string +to_string( basic_string_view v, Allocator const & a = Allocator() ) +{ + return std::basic_string( v.begin(), v.end(), a ); +} #else - template< class CharT, class Traits > - std::basic_string - to_string(basic_string_view v) - { - return std::basic_string(v.begin(), v.end()); - } +template< class CharT, class Traits > +std::basic_string +to_string( basic_string_view v ) +{ + return std::basic_string( v.begin(), v.end() ); +} - template< class CharT, class Traits, class Allocator > - std::basic_string - to_string(basic_string_view v, Allocator const& a) - { - return std::basic_string(v.begin(), v.end(), a); - } +template< class CharT, class Traits, class Allocator > +std::basic_string +to_string( basic_string_view v, Allocator const & a ) +{ + return std::basic_string( v.begin(), v.end(), a ); +} #endif // nssv_CPP11_OR_GREATER - template< class CharT, class Traits, class Allocator > - basic_string_view - to_string_view(std::basic_string const& s) - { - return basic_string_view(s.data(), s.size()); - } +template< class CharT, class Traits, class Allocator > +basic_string_view +to_string_view( std::basic_string const & s ) +{ + return basic_string_view( s.data(), s.size() ); +} - } -} // namespace nonstd::sv_lite +}} // namespace nonstd::sv_lite #endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS @@ -2250,33 +2287,33 @@ namespace nonstd { namespace nonstd { - using sv_lite::basic_string_view; - using sv_lite::string_view; - using sv_lite::wstring_view; +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; +using sv_lite::u16string_view; #endif #if nssv_HAVE_WCHAR32_T - using sv_lite::u32string_view; +using sv_lite::u32string_view; #endif - // literal "sv" +// 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>=; +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<<; +using sv_lite::operator<<; #endif #if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS - using sv_lite::to_string; - using sv_lite::to_string_view; +using sv_lite::to_string; +using sv_lite::to_string_view; #endif } // namespace nonstd @@ -2292,45 +2329,45 @@ namespace nonstd { namespace std { - template<> - struct hash< nonstd::string_view > +template<> +struct hash< nonstd::string_view > +{ +public: + std::size_t operator()( nonstd::string_view v ) const nssv_noexcept { - public: - std::size_t operator()(nonstd::string_view v) const nssv_noexcept - { - return std::hash()(std::string(v.data(), v.size())); - } - }; + return std::hash()( std::string( v.data(), v.size() ) ); + } +}; - template<> - struct hash< nonstd::wstring_view > +template<> +struct hash< nonstd::wstring_view > +{ +public: + std::size_t operator()( nonstd::wstring_view v ) const nssv_noexcept { - public: - std::size_t operator()(nonstd::wstring_view v) const nssv_noexcept - { - return std::hash()(std::wstring(v.data(), v.size())); - } - }; + return std::hash()( std::wstring( v.data(), v.size() ) ); + } +}; - template<> - struct hash< nonstd::u16string_view > +template<> +struct hash< nonstd::u16string_view > +{ +public: + std::size_t operator()( nonstd::u16string_view v ) const nssv_noexcept { - public: - std::size_t operator()(nonstd::u16string_view v) const nssv_noexcept - { - return std::hash()(std::u16string(v.data(), v.size())); - } - }; + return std::hash()( std::u16string( v.data(), v.size() ) ); + } +}; - template<> - struct hash< nonstd::u32string_view > +template<> +struct hash< nonstd::u32string_view > +{ +public: + std::size_t operator()( nonstd::u32string_view v ) const nssv_noexcept { - public: - std::size_t operator()(nonstd::u32string_view v) const nssv_noexcept - { - return std::hash()(std::u32string(v.data(), v.size())); - } - }; + return std::hash()( std::u32string( v.data(), v.size() ) ); + } +}; } // namespace std @@ -2344,7 +2381,7 @@ nssv_RESTORE_WARNINGS() SIMDJSON_POP_DISABLE_WARNINGS namespace std { - using string_view = nonstd::string_view; + 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. @@ -2362,16 +2399,27 @@ namespace std { // 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. -#ifdef _DEBUG +// 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. -#ifndef __OPTIMIZE__ +// 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 @@ -2427,6 +2475,18 @@ namespace std { #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 */ /* skipped duplicate #include "simdjson/compiler_check.h" */ @@ -2442,362 +2502,730 @@ namespace std { 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 - 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. - 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 - NUM_ERROR_CODES - }; +/** + * 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. + NUM_ERROR_CODES ///< Placeholder for end of error code list. +}; - /** - * 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. - */ +/** + * 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; - /** - * 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; +/** + * 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. + */ - /** - * Write the error message to the output stream - */ - inline std::ostream& operator<<(std::ostream& out, error_code error) noexcept; +/** + * 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; - /** - * 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 { return error_message(error()); } - /** The error code */ - error_code error() const noexcept { return _error; } - private: - /** The error code that was used */ - error_code _error; - }; +/** + * Write the error message to the output stream + */ +inline std::ostream& operator<<(std::ostream& out, error_code error) noexcept; - namespace internal { +/** + * 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; +}; - /** - * 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 : public internal::simdjson_result_base { - * simdjson_result() noexcept : internal::simdjson_result_base() {} - * simdjson_result(error_code error) noexcept : internal::simdjson_result_base(error) {} - * simdjson_result(T &&value) noexcept : internal::simdjson_result_base(std::forward(value)) {} - * simdjson_result(T &&value, error_code error) noexcept : internal::simdjson_result_base(value, error) {} - * // Your extra methods here - * } - * - * Then any method returning simdjson_result will be chainable with your methods. - */ - template - struct simdjson_result_base : protected std::pair { +namespace internal { - /** - * Create a new empty result with error = UNINITIALIZED. - */ - simdjson_inline simdjson_result_base() noexcept; +/** + * The result of a simdjson operation that could fail. + * + * IMPORTANT: For the ondemand API, we use implementation_simdjson_result_base as a base class + * to avoid some compilation issue. Thus, if you modify this class, please ensure that the ondemand + * implementation_simdjson_result_base 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 : public internal::simdjson_result_base { + * simdjson_result() noexcept : internal::simdjson_result_base() {} + * simdjson_result(error_code error) noexcept : internal::simdjson_result_base(error) {} + * simdjson_result(T &&value) noexcept : internal::simdjson_result_base(std::forward(value)) {} + * simdjson_result(T &&value, error_code error) noexcept : internal::simdjson_result_base(value, error) {} + * // Your extra methods here + * } + * + * Then any method returning simdjson_result will be chainable with your methods. + */ +template +struct simdjson_result_base : protected std::pair { - /** - * Create a new error result. - */ - simdjson_inline simdjson_result_base(error_code error) noexcept; + /** + * Create a new empty result with error = UNINITIALIZED. + */ + simdjson_inline simdjson_result_base() noexcept; - /** - * Create a new successful result. - */ - simdjson_inline simdjson_result_base(T&& value) noexcept; + /** + * Create a new error result. + */ + simdjson_inline simdjson_result_base(error_code error) 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; + /** + * Create a new successful result. + */ + simdjson_inline simdjson_result_base(T &&value) 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; + /** + * 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 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; + /** + * 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; - /** - * The error. - */ - simdjson_inline error_code error() const 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 - /** - * Get the result value. - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline T& value() & noexcept(false); + /** + * 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); - /** - * Take the result value (move it). - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline T&& value() && 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&& take_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); - /** - * 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. + * 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. - */ - 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. + * 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; - }; // struct simdjson_result_base + using value_type = T; + using error_type = error_code; +}; // struct simdjson_result_base - } // namespace internal +} // 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 - struct simdjson_result : public internal::simdjson_result_base { - /** - * @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; +/** + * 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 +struct simdjson_result : public internal::simdjson_result_base { - /** - * 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; + /** + * @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 + simdjson_warn_unused simdjson_inline error_code get(std::string &value) && noexcept { + static_assert(std::is_same::value, "SFINAE"); + std::string_view v; + error_code error = std::forward>(*this).get(v); + if (!error) { + value.assign(v.data(), v.size()); + } + return error; + } + + /** + * The error. + */ + simdjson_inline error_code error() const 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_inline error_code error() const noexcept; #if SIMDJSON_EXCEPTIONS + using internal::simdjson_result_base::operator*; + using internal::simdjson_result_base::operator->; + /** + * Get the result value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T& value() & 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&& value() && noexcept(false); + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& take_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); + /** + * 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 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; + /** + * 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; - }; // struct simdjson_result + using value_type = T; + using error_type = error_code; +}; // struct simdjson_result #if SIMDJSON_EXCEPTIONS - template - inline std::ostream& operator<<(std::ostream& out, simdjson_result value) { return out << value.value(); } +template +inline std::ostream& operator<<(std::ostream& out, simdjson_result 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 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; +/** + * @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 +#include + +namespace simdjson { +namespace concepts { + +namespace details { +#define SIMDJSON_IMPL_CONCEPT(name, method) \ + template \ + concept supports_##name = !std::is_const_v && requires { \ + typename std::remove_cvref_t::value_type; \ + requires requires(typename std::remove_cvref_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 +concept is_pair = requires { typename T::first_type; typename T::second_type; } && + std::same_as>; +template +concept string_view_like = std::is_convertible_v && + !std::is_convertible_v; + +template +concept constructible_from_string_view = std::is_constructible_v + && !std::is_same_v + && std::is_default_constructible_v; + +template +concept string_view_keyed_map = string_view_like + && requires(std::remove_cvref_t& m, typename M::key_type sv, typename M::mapped_type v) { + { m.emplace(sv, v) } -> std::same_as>; +}; + +/// Check if T is a container that we can append to, including: +/// std::vector, std::deque, std::list, std::string, ... +template +concept appendable_containers = + (details::supports_emplace_back || details::supports_emplace || + details::supports_push_back || details::supports_push || + details::supports_add || details::supports_append || + details::supports_insert) && !string_view_keyed_map; + +/// Insert into the container however possible +template +constexpr decltype(auto) emplace_one(T &vec, Args &&...args) { + if constexpr (details::supports_emplace_back) { + return vec.emplace_back(std::forward(args)...); + } else if constexpr (details::supports_emplace) { + return vec.emplace(std::forward(args)...); + } else if constexpr (details::supports_push_back) { + return vec.push_back(std::forward(args)...); + } else if constexpr (details::supports_push) { + return vec.push(std::forward(args)...); + } else if constexpr (details::supports_add) { + return vec.add(std::forward(args)...); + } else if constexpr (details::supports_append) { + return vec.append(std::forward(args)...); + } else if constexpr (details::supports_insert) { + return vec.insert(std::forward(args)...); + } else if constexpr (details::supports_op_append && sizeof...(Args) == 1) { + return vec.operator+=(std::forward(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 +concept returns_reference = appendable_containers && requires { + typename std::remove_cvref_t::reference; + requires requires(typename std::remove_cvref_t::value_type &&val, T obj) { + { + emplace_one(obj, std::move(val)) + } -> std::same_as::reference>; + }; +}; + +template +concept smart_pointer = requires(std::remove_cvref_t ptr) { + // Check if T has a member type named element_type + typename std::remove_cvref_t::element_type; + + // Check if T has a get() member function + { + ptr.get() + } -> std::same_as::element_type *>; + + // Check if T can be dereferenced + { *ptr } -> std::same_as::element_type &>; +}; + +template +concept optional_type = requires(std::remove_cvref_t obj) { + typename std::remove_cvref_t::value_type; + { obj.value() } -> std::same_as::value_type&>; + requires requires(typename std::remove_cvref_t::value_type &&val) { + obj.emplace(std::move(val)); + { + obj.value_or(val) + } -> std::convertible_to::value_type>; + }; + { static_cast(obj) } -> std::same_as; // convertible to bool + { obj.reset() } noexcept -> std::same_as; +}; + + + +// Types we serialize as JSON strings (not as containers) +template +concept string_like = + std::is_same_v, std::string> || + std::is_same_v, std::string_view> || + std::is_same_v, const char*> || + std::is_same_v, 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 +template +concept container_but_not_string = + std::ranges::input_range && !string_like && !concepts::string_view_keyed_map; + + + +// 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 +concept indexable_container = requires { + typename Container::value_type; + requires !concepts::string_like; + requires !requires { typename Container::key_type; }; // Reject maps/sets + requires requires(Container& c, std::size_t i) { + { c[i] } -> std::convertible_to; + }; +}; + + +// Variable template to use with std::meta::substitute +template +constexpr bool indexable_container_v = indexable_container; + +} // namespace concepts + + +/** + * We use tag_invoke as our customization point mechanism. + */ +template +concept tag_invocable = requires(Tag tag, Args... args) { + tag_invoke(std::forward(tag), std::forward(args)...); +}; + +template +concept nothrow_tag_invocable = + tag_invocable && requires(Tag tag, Args... args) { + { + tag_invoke(std::forward(tag), std::forward(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 +#include +#include + +namespace simdjson { +namespace constevalutil { +#if SIMDJSON_CONSTEVAL + +constexpr static std::array 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 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; +} +#endif // SIMDJSON_CONSTEVAL + + +#if SIMDJSON_SUPPORTS_CONCEPTS +template +struct fixed_string { + constexpr fixed_string(const char (&str)[N]) { + for (std::size_t i = 0; i < N; ++i) { + data[i] = 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 +fixed_string(const char (&)[N]) -> fixed_string; + +template +struct string_constant { + static constexpr std::string_view value = str.view(); +}; +#endif // SIMDJSON_SUPPORTS_CONCEPTS + +} // namespace constevalutil +} // namespace simdjson +#endif // SIMDJSON_CONSTEVALUTIL_H +/* end file simdjson/constevalutil.h */ /** * @brief The top level simdjson namespace, containing everything the library provides. */ namespace simdjson { - SIMDJSON_PUSH_DISABLE_UNUSED_WARNINGS +SIMDJSON_PUSH_DISABLE_UNUSED_WARNINGS - /** The maximum document size supported by simdjson. */ - constexpr size_t SIMDJSON_MAXSIZE_BYTES = 0xFFFFFFFF; +/** The maximum document size supported by simdjson. */ +constexpr size_t SIMDJSON_MAXSIZE_BYTES = 0xFFFFFFFF; - /** - * 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; +/** + * 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; +/** + * 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 +SIMDJSON_POP_DISABLE_UNUSED_WARNINGS - class implementation; - struct padded_string; - class padded_string_view; - enum class stage1_mode; +class implementation; +struct padded_string; +class padded_string_view; +enum class stage1_mode; - namespace internal { +namespace internal { - template - class atomic_ptr; - class dom_parser_implementation; - class escape_json_string; - class tape_ref; - struct value128; - enum class tape_type; +template +class atomic_ptr; +class dom_parser_implementation; +class escape_json_string; +class tape_ref; +struct value128; +enum class tape_type; - } // namespace internal +} // namespace internal } // namespace simdjson #endif // SIMDJSON_BASE_H @@ -2821,958 +3249,940 @@ SIMDJSON_PUSH_DISABLE_UNUSED_WARNINGS #include namespace simdjson { - namespace internal { - /*! - implements the Grisu2 algorithm for binary to decimal floating-point - conversion. - Adapted from JSON for Modern C++ +namespace internal { +/*! +implements the Grisu2 algorithm for binary to decimal floating-point +conversion. +Adapted from JSON for Modern C++ - This implementation is a slightly modified version of the reference - implementation which may be obtained from - http://florian.loitsch.com/publications (bench.tar.gz). - The code is distributed under the MIT license, Copyright (c) 2009 Florian - Loitsch. For a detailed description of the algorithm see: [1] Loitsch, "Printing - Floating-Point Numbers Quickly and Accurately with Integers", Proceedings of the - ACM SIGPLAN 2010 Conference on Programming Language Design and Implementation, - PLDI 2010 [2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and - Accurately", Proceedings of the ACM SIGPLAN 1996 Conference on Programming - Language Design and Implementation, PLDI 1996 - */ - namespace dtoa_impl { +This implementation is a slightly modified version of the reference +implementation which may be obtained from +http://florian.loitsch.com/publications (bench.tar.gz). +The code is distributed under the MIT license, Copyright (c) 2009 Florian +Loitsch. For a detailed description of the algorithm see: [1] Loitsch, "Printing +Floating-Point Numbers Quickly and Accurately with Integers", Proceedings of the +ACM SIGPLAN 2010 Conference on Programming Language Design and Implementation, +PLDI 2010 [2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and +Accurately", Proceedings of the ACM SIGPLAN 1996 Conference on Programming +Language Design and Implementation, PLDI 1996 +*/ +namespace dtoa_impl { - template - Target reinterpret_bits(const Source source) { - static_assert(sizeof(Target) == sizeof(Source), "size mismatch"); +template +Target reinterpret_bits(const Source source) { + static_assert(sizeof(Target) == sizeof(Source), "size mismatch"); - Target target; - std::memcpy(&target, &source, sizeof(Source)); - return target; - } + Target target; + std::memcpy(&target, &source, sizeof(Source)); + return target; +} - struct diyfp // f * 2^e - { - static constexpr int kPrecision = 64; // = q +struct diyfp // f * 2^e +{ + static constexpr int kPrecision = 64; // = q - std::uint64_t f = 0; - int e = 0; + std::uint64_t f = 0; + int e = 0; - constexpr diyfp(std::uint64_t f_, int e_) noexcept : f(f_), e(e_) {} + constexpr diyfp(std::uint64_t f_, int e_) noexcept : f(f_), e(e_) {} - /*! - @brief returns x - y - @pre x.e == y.e and x.f >= y.f - */ - static diyfp sub(const diyfp& x, const diyfp& y) noexcept { + /*! + @brief returns x - y + @pre x.e == y.e and x.f >= y.f + */ + static diyfp sub(const diyfp &x, const diyfp &y) noexcept { - return { x.f - y.f, x.e }; - } + return {x.f - y.f, x.e}; + } - /*! - @brief returns x * y - @note The result is rounded. (Only the upper q bits are returned.) - */ - static diyfp mul(const diyfp& x, const diyfp& y) noexcept { - static_assert(kPrecision == 64, "internal error"); + /*! + @brief returns x * y + @note The result is rounded. (Only the upper q bits are returned.) + */ + static diyfp mul(const diyfp &x, const diyfp &y) noexcept { + static_assert(kPrecision == 64, "internal error"); - // Computes: - // f = round((x.f * y.f) / 2^q) - // e = x.e + y.e + q + // Computes: + // f = round((x.f * y.f) / 2^q) + // e = x.e + y.e + q - // Emulate the 64-bit * 64-bit multiplication: - // - // p = u * v - // = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi) - // = (u_lo v_lo ) + 2^32 ((u_lo v_hi ) + (u_hi v_lo )) + - // 2^64 (u_hi v_hi ) = (p0 ) + 2^32 ((p1 ) + (p2 )) - // + 2^64 (p3 ) = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + - // 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3 ) = - // (p0_lo ) + 2^32 (p0_hi + p1_lo + p2_lo ) + 2^64 (p1_hi + - // p2_hi + p3) = (p0_lo ) + 2^32 (Q ) + 2^64 (H ) = (p0_lo ) + - // 2^32 (Q_lo + 2^32 Q_hi ) + 2^64 (H ) - // - // (Since Q might be larger than 2^32 - 1) - // - // = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H) - // - // (Q_hi + H does not overflow a 64-bit int) - // - // = p_lo + 2^64 p_hi + // Emulate the 64-bit * 64-bit multiplication: + // + // p = u * v + // = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi) + // = (u_lo v_lo ) + 2^32 ((u_lo v_hi ) + (u_hi v_lo )) + + // 2^64 (u_hi v_hi ) = (p0 ) + 2^32 ((p1 ) + (p2 )) + // + 2^64 (p3 ) = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + + // 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3 ) = + // (p0_lo ) + 2^32 (p0_hi + p1_lo + p2_lo ) + 2^64 (p1_hi + + // p2_hi + p3) = (p0_lo ) + 2^32 (Q ) + 2^64 (H ) = (p0_lo ) + + // 2^32 (Q_lo + 2^32 Q_hi ) + 2^64 (H ) + // + // (Since Q might be larger than 2^32 - 1) + // + // = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H) + // + // (Q_hi + H does not overflow a 64-bit int) + // + // = p_lo + 2^64 p_hi - const std::uint64_t u_lo = x.f & 0xFFFFFFFFu; - const std::uint64_t u_hi = x.f >> 32u; - const std::uint64_t v_lo = y.f & 0xFFFFFFFFu; - const std::uint64_t v_hi = y.f >> 32u; + const std::uint64_t u_lo = x.f & 0xFFFFFFFFu; + const std::uint64_t u_hi = x.f >> 32u; + const std::uint64_t v_lo = y.f & 0xFFFFFFFFu; + const std::uint64_t v_hi = y.f >> 32u; - const std::uint64_t p0 = u_lo * v_lo; - const std::uint64_t p1 = u_lo * v_hi; - const std::uint64_t p2 = u_hi * v_lo; - const std::uint64_t p3 = u_hi * v_hi; + const std::uint64_t p0 = u_lo * v_lo; + const std::uint64_t p1 = u_lo * v_hi; + const std::uint64_t p2 = u_hi * v_lo; + const std::uint64_t p3 = u_hi * v_hi; - const std::uint64_t p0_hi = p0 >> 32u; - const std::uint64_t p1_lo = p1 & 0xFFFFFFFFu; - const std::uint64_t p1_hi = p1 >> 32u; - const std::uint64_t p2_lo = p2 & 0xFFFFFFFFu; - const std::uint64_t p2_hi = p2 >> 32u; + const std::uint64_t p0_hi = p0 >> 32u; + const std::uint64_t p1_lo = p1 & 0xFFFFFFFFu; + const std::uint64_t p1_hi = p1 >> 32u; + const std::uint64_t p2_lo = p2 & 0xFFFFFFFFu; + const std::uint64_t p2_hi = p2 >> 32u; - std::uint64_t Q = p0_hi + p1_lo + p2_lo; + std::uint64_t Q = p0_hi + p1_lo + p2_lo; - // The full product might now be computed as - // - // p_hi = p3 + p2_hi + p1_hi + (Q >> 32) - // p_lo = p0_lo + (Q << 32) - // - // But in this particular case here, the full p_lo is not required. - // Effectively we only need to add the highest bit in p_lo to p_hi (and - // Q_hi + 1 does not overflow). + // The full product might now be computed as + // + // p_hi = p3 + p2_hi + p1_hi + (Q >> 32) + // p_lo = p0_lo + (Q << 32) + // + // But in this particular case here, the full p_lo is not required. + // Effectively we only need to add the highest bit in p_lo to p_hi (and + // Q_hi + 1 does not overflow). - Q += std::uint64_t{ 1 } << (64u - 32u - 1u); // round, ties up + Q += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up - const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u); + const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u); - return { h, x.e + y.e + 64 }; - } + return {h, x.e + y.e + 64}; + } - /*! - @brief normalize x such that the significand is >= 2^(q-1) - @pre x.f != 0 - */ - static diyfp normalize(diyfp x) noexcept { + /*! + @brief normalize x such that the significand is >= 2^(q-1) + @pre x.f != 0 + */ + static diyfp normalize(diyfp x) noexcept { - while ((x.f >> 63u) == 0) { - x.f <<= 1u; - x.e--; - } + while ((x.f >> 63u) == 0) { + x.f <<= 1u; + x.e--; + } - return x; - } + return x; + } - /*! - @brief normalize x such that the result has the exponent E - @pre e >= x.e and the upper e - x.e bits of x.f must be zero. - */ - static diyfp normalize_to(const diyfp& x, - const int target_exponent) noexcept { - const int delta = x.e - target_exponent; + /*! + @brief normalize x such that the result has the exponent E + @pre e >= x.e and the upper e - x.e bits of x.f must be zero. + */ + static diyfp normalize_to(const diyfp &x, + const int target_exponent) noexcept { + const int delta = x.e - target_exponent; - return { x.f << delta, target_exponent }; - } - }; + return {x.f << delta, target_exponent}; + } +}; - struct boundaries { - diyfp w; - diyfp minus; - diyfp plus; - }; +struct boundaries { + diyfp w; + diyfp minus; + diyfp plus; +}; - /*! - Compute the (normalized) diyfp representing the input number 'value' and its - boundaries. - @pre value must be finite and positive - */ - template boundaries compute_boundaries(FloatType value) { +/*! +Compute the (normalized) diyfp representing the input number 'value' and its +boundaries. +@pre value must be finite and positive +*/ +template boundaries compute_boundaries(FloatType value) { - // Convert the IEEE representation into a diyfp. - // - // If v is denormal: - // value = 0.F * 2^(1 - bias) = ( F) * 2^(1 - bias - (p-1)) - // If v is normalized: - // value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1)) + // Convert the IEEE representation into a diyfp. + // + // If v is denormal: + // value = 0.F * 2^(1 - bias) = ( F) * 2^(1 - bias - (p-1)) + // If v is normalized: + // value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1)) - static_assert(std::numeric_limits::is_iec559, - "internal error: dtoa_short requires an IEEE-754 " - "floating-point implementation"); + static_assert(std::numeric_limits::is_iec559, + "internal error: dtoa_short requires an IEEE-754 " + "floating-point implementation"); - constexpr int kPrecision = - std::numeric_limits::digits; // = p (includes the hidden bit) - constexpr int kBias = - std::numeric_limits::max_exponent - 1 + (kPrecision - 1); - constexpr int kMinExp = 1 - kBias; - constexpr std::uint64_t kHiddenBit = std::uint64_t{ 1 } - << (kPrecision - 1); // = 2^(p-1) + constexpr int kPrecision = + std::numeric_limits::digits; // = p (includes the hidden bit) + constexpr int kBias = + std::numeric_limits::max_exponent - 1 + (kPrecision - 1); + constexpr int kMinExp = 1 - kBias; + constexpr std::uint64_t kHiddenBit = std::uint64_t{1} + << (kPrecision - 1); // = 2^(p-1) - using bits_type = typename std::conditional::type; + using bits_type = typename std::conditional::type; - const std::uint64_t bits = reinterpret_bits(value); - const std::uint64_t E = bits >> (kPrecision - 1); - const std::uint64_t F = bits & (kHiddenBit - 1); + const std::uint64_t bits = reinterpret_bits(value); + const std::uint64_t E = bits >> (kPrecision - 1); + const std::uint64_t F = bits & (kHiddenBit - 1); - const bool is_denormal = E == 0; - const diyfp v = is_denormal - ? diyfp(F, kMinExp) - : diyfp(F + kHiddenBit, static_cast(E) - kBias); + const bool is_denormal = E == 0; + const diyfp v = is_denormal + ? diyfp(F, kMinExp) + : diyfp(F + kHiddenBit, static_cast(E) - kBias); - // Compute the boundaries m- and m+ of the floating-point value - // v = f * 2^e. - // - // Determine v- and v+, the floating-point predecessor and successor if v, - // respectively. - // - // v- = v - 2^e if f != 2^(p-1) or e == e_min (A) - // = v - 2^(e-1) if f == 2^(p-1) and e > e_min (B) - // - // v+ = v + 2^e - // - // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_ - // between m- and m+ round to v, regardless of how the input rounding - // algorithm breaks ties. - // - // ---+-------------+-------------+-------------+-------------+--- (A) - // v- m- v m+ v+ - // - // -----------------+------+------+-------------+-------------+--- (B) - // v- m- v m+ v+ + // Compute the boundaries m- and m+ of the floating-point value + // v = f * 2^e. + // + // Determine v- and v+, the floating-point predecessor and successor if v, + // respectively. + // + // v- = v - 2^e if f != 2^(p-1) or e == e_min (A) + // = v - 2^(e-1) if f == 2^(p-1) and e > e_min (B) + // + // v+ = v + 2^e + // + // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_ + // between m- and m+ round to v, regardless of how the input rounding + // algorithm breaks ties. + // + // ---+-------------+-------------+-------------+-------------+--- (A) + // v- m- v m+ v+ + // + // -----------------+------+------+-------------+-------------+--- (B) + // v- m- v m+ v+ - const bool lower_boundary_is_closer = F == 0 && E > 1; - const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1); - const diyfp m_minus = lower_boundary_is_closer - ? diyfp(4 * v.f - 1, v.e - 2) // (B) - : diyfp(2 * v.f - 1, v.e - 1); // (A) + const bool lower_boundary_is_closer = F == 0 && E > 1; + const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1); + const diyfp m_minus = lower_boundary_is_closer + ? diyfp(4 * v.f - 1, v.e - 2) // (B) + : diyfp(2 * v.f - 1, v.e - 1); // (A) - // Determine the normalized w+ = m+. - const diyfp w_plus = diyfp::normalize(m_plus); + // Determine the normalized w+ = m+. + const diyfp w_plus = diyfp::normalize(m_plus); - // Determine w- = m- such that e_(w-) = e_(w+). - const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e); + // Determine w- = m- such that e_(w-) = e_(w+). + const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e); - return { diyfp::normalize(v), w_minus, w_plus }; - } + return {diyfp::normalize(v), w_minus, w_plus}; +} - // Given normalized diyfp w, Grisu needs to find a (normalized) cached - // power-of-ten c, such that the exponent of the product c * w = f * 2^e lies - // within a certain range [alpha, gamma] (Definition 3.2 from [1]) - // - // alpha <= e = e_c + e_w + q <= gamma - // - // or - // - // f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q - // <= f_c * f_w * 2^gamma - // - // Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies - // - // 2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma - // - // or - // - // 2^(q - 2 + alpha) <= c * w < 2^(q + gamma) - // - // The choice of (alpha,gamma) determines the size of the table and the form of - // the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well - // in practice: - // - // The idea is to cut the number c * w = f * 2^e into two parts, which can be - // processed independently: An integral part p1, and a fractional part p2: - // - // f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e - // = (f div 2^-e) + (f mod 2^-e) * 2^e - // = p1 + p2 * 2^e - // - // The conversion of p1 into decimal form requires a series of divisions and - // modulos by (a power of) 10. These operations are faster for 32-bit than for - // 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be - // achieved by choosing - // - // -e >= 32 or e <= -32 := gamma - // - // In order to convert the fractional part - // - // p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ... - // - // into decimal form, the fraction is repeatedly multiplied by 10 and the digits - // d[-i] are extracted in order: - // - // (10 * p2) div 2^-e = d[-1] - // (10 * p2) mod 2^-e = d[-2] / 10^1 + ... - // - // The multiplication by 10 must not overflow. It is sufficient to choose - // - // 10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64. - // - // Since p2 = f mod 2^-e < 2^-e, - // - // -e <= 60 or e >= -60 := alpha +// Given normalized diyfp w, Grisu needs to find a (normalized) cached +// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies +// within a certain range [alpha, gamma] (Definition 3.2 from [1]) +// +// alpha <= e = e_c + e_w + q <= gamma +// +// or +// +// f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q +// <= f_c * f_w * 2^gamma +// +// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies +// +// 2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma +// +// or +// +// 2^(q - 2 + alpha) <= c * w < 2^(q + gamma) +// +// The choice of (alpha,gamma) determines the size of the table and the form of +// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well +// in practice: +// +// The idea is to cut the number c * w = f * 2^e into two parts, which can be +// processed independently: An integral part p1, and a fractional part p2: +// +// f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e +// = (f div 2^-e) + (f mod 2^-e) * 2^e +// = p1 + p2 * 2^e +// +// The conversion of p1 into decimal form requires a series of divisions and +// modulos by (a power of) 10. These operations are faster for 32-bit than for +// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be +// achieved by choosing +// +// -e >= 32 or e <= -32 := gamma +// +// In order to convert the fractional part +// +// p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ... +// +// into decimal form, the fraction is repeatedly multiplied by 10 and the digits +// d[-i] are extracted in order: +// +// (10 * p2) div 2^-e = d[-1] +// (10 * p2) mod 2^-e = d[-2] / 10^1 + ... +// +// The multiplication by 10 must not overflow. It is sufficient to choose +// +// 10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64. +// +// Since p2 = f mod 2^-e < 2^-e, +// +// -e <= 60 or e >= -60 := alpha - constexpr int kAlpha = -60; - constexpr int kGamma = -32; +constexpr int kAlpha = -60; +constexpr int kGamma = -32; - struct cached_power // c = f * 2^e ~= 10^k - { - std::uint64_t f; - int e; - int k; - }; +struct cached_power // c = f * 2^e ~= 10^k +{ + std::uint64_t f; + int e; + int k; +}; - /*! - For a normalized diyfp w = f * 2^e, this function returns a (normalized) cached - power-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c - satisfies (Definition 3.2 from [1]) - alpha <= e_c + e + q <= gamma. - */ - inline cached_power get_cached_power_for_binary_exponent(int e) { - // Now - // - // alpha <= e_c + e + q <= gamma (1) - // ==> f_c * 2^alpha <= c * 2^e * 2^q - // - // and since the c's are normalized, 2^(q-1) <= f_c, - // - // ==> 2^(q - 1 + alpha) <= c * 2^(e + q) - // ==> 2^(alpha - e - 1) <= c - // - // If c were an exact power of ten, i.e. c = 10^k, one may determine k as - // - // k = ceil( log_10( 2^(alpha - e - 1) ) ) - // = ceil( (alpha - e - 1) * log_10(2) ) - // - // From the paper: - // "In theory the result of the procedure could be wrong since c is rounded, - // and the computation itself is approximated [...]. In practice, however, - // this simple function is sufficient." - // - // For IEEE double precision floating-point numbers converted into - // normalized diyfp's w = f * 2^e, with q = 64, - // - // e >= -1022 (min IEEE exponent) - // -52 (p - 1) - // -52 (p - 1, possibly normalize denormal IEEE numbers) - // -11 (normalize the diyfp) - // = -1137 - // - // and - // - // e <= +1023 (max IEEE exponent) - // -52 (p - 1) - // -11 (normalize the diyfp) - // = 960 - // - // This binary exponent range [-1137,960] results in a decimal exponent - // range [-307,324]. One does not need to store a cached power for each - // k in this range. For each such k it suffices to find a cached power - // such that the exponent of the product lies in [alpha,gamma]. - // This implies that the difference of the decimal exponents of adjacent - // table entries must be less than or equal to - // - // floor( (gamma - alpha) * log_10(2) ) = 8. - // - // (A smaller distance gamma-alpha would require a larger table.) +/*! +For a normalized diyfp w = f * 2^e, this function returns a (normalized) cached +power-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c +satisfies (Definition 3.2 from [1]) + alpha <= e_c + e + q <= gamma. +*/ +inline cached_power get_cached_power_for_binary_exponent(int e) { + // Now + // + // alpha <= e_c + e + q <= gamma (1) + // ==> f_c * 2^alpha <= c * 2^e * 2^q + // + // and since the c's are normalized, 2^(q-1) <= f_c, + // + // ==> 2^(q - 1 + alpha) <= c * 2^(e + q) + // ==> 2^(alpha - e - 1) <= c + // + // If c were an exact power of ten, i.e. c = 10^k, one may determine k as + // + // k = ceil( log_10( 2^(alpha - e - 1) ) ) + // = ceil( (alpha - e - 1) * log_10(2) ) + // + // From the paper: + // "In theory the result of the procedure could be wrong since c is rounded, + // and the computation itself is approximated [...]. In practice, however, + // this simple function is sufficient." + // + // For IEEE double precision floating-point numbers converted into + // normalized diyfp's w = f * 2^e, with q = 64, + // + // e >= -1022 (min IEEE exponent) + // -52 (p - 1) + // -52 (p - 1, possibly normalize denormal IEEE numbers) + // -11 (normalize the diyfp) + // = -1137 + // + // and + // + // e <= +1023 (max IEEE exponent) + // -52 (p - 1) + // -11 (normalize the diyfp) + // = 960 + // + // This binary exponent range [-1137,960] results in a decimal exponent + // range [-307,324]. One does not need to store a cached power for each + // k in this range. For each such k it suffices to find a cached power + // such that the exponent of the product lies in [alpha,gamma]. + // This implies that the difference of the decimal exponents of adjacent + // table entries must be less than or equal to + // + // floor( (gamma - alpha) * log_10(2) ) = 8. + // + // (A smaller distance gamma-alpha would require a larger table.) - // NB: - // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34. + // NB: + // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34. - constexpr int kCachedPowersMinDecExp = -300; - constexpr int kCachedPowersDecStep = 8; + constexpr int kCachedPowersMinDecExp = -300; + constexpr int kCachedPowersDecStep = 8; - static constexpr std::array kCachedPowers = { { - {0xAB70FE17C79AC6CA, -1060, -300}, {0xFF77B1FCBEBCDC4F, -1034, -292}, - {0xBE5691EF416BD60C, -1007, -284}, {0x8DD01FAD907FFC3C, -980, -276}, - {0xD3515C2831559A83, -954, -268}, {0x9D71AC8FADA6C9B5, -927, -260}, - {0xEA9C227723EE8BCB, -901, -252}, {0xAECC49914078536D, -874, -244}, - {0x823C12795DB6CE57, -847, -236}, {0xC21094364DFB5637, -821, -228}, - {0x9096EA6F3848984F, -794, -220}, {0xD77485CB25823AC7, -768, -212}, - {0xA086CFCD97BF97F4, -741, -204}, {0xEF340A98172AACE5, -715, -196}, - {0xB23867FB2A35B28E, -688, -188}, {0x84C8D4DFD2C63F3B, -661, -180}, - {0xC5DD44271AD3CDBA, -635, -172}, {0x936B9FCEBB25C996, -608, -164}, - {0xDBAC6C247D62A584, -582, -156}, {0xA3AB66580D5FDAF6, -555, -148}, - {0xF3E2F893DEC3F126, -529, -140}, {0xB5B5ADA8AAFF80B8, -502, -132}, - {0x87625F056C7C4A8B, -475, -124}, {0xC9BCFF6034C13053, -449, -116}, - {0x964E858C91BA2655, -422, -108}, {0xDFF9772470297EBD, -396, -100}, - {0xA6DFBD9FB8E5B88F, -369, -92}, {0xF8A95FCF88747D94, -343, -84}, - {0xB94470938FA89BCF, -316, -76}, {0x8A08F0F8BF0F156B, -289, -68}, - {0xCDB02555653131B6, -263, -60}, {0x993FE2C6D07B7FAC, -236, -52}, - {0xE45C10C42A2B3B06, -210, -44}, {0xAA242499697392D3, -183, -36}, - {0xFD87B5F28300CA0E, -157, -28}, {0xBCE5086492111AEB, -130, -20}, - {0x8CBCCC096F5088CC, -103, -12}, {0xD1B71758E219652C, -77, -4}, - {0x9C40000000000000, -50, 4}, {0xE8D4A51000000000, -24, 12}, - {0xAD78EBC5AC620000, 3, 20}, {0x813F3978F8940984, 30, 28}, - {0xC097CE7BC90715B3, 56, 36}, {0x8F7E32CE7BEA5C70, 83, 44}, - {0xD5D238A4ABE98068, 109, 52}, {0x9F4F2726179A2245, 136, 60}, - {0xED63A231D4C4FB27, 162, 68}, {0xB0DE65388CC8ADA8, 189, 76}, - {0x83C7088E1AAB65DB, 216, 84}, {0xC45D1DF942711D9A, 242, 92}, - {0x924D692CA61BE758, 269, 100}, {0xDA01EE641A708DEA, 295, 108}, - {0xA26DA3999AEF774A, 322, 116}, {0xF209787BB47D6B85, 348, 124}, - {0xB454E4A179DD1877, 375, 132}, {0x865B86925B9BC5C2, 402, 140}, - {0xC83553C5C8965D3D, 428, 148}, {0x952AB45CFA97A0B3, 455, 156}, - {0xDE469FBD99A05FE3, 481, 164}, {0xA59BC234DB398C25, 508, 172}, - {0xF6C69A72A3989F5C, 534, 180}, {0xB7DCBF5354E9BECE, 561, 188}, - {0x88FCF317F22241E2, 588, 196}, {0xCC20CE9BD35C78A5, 614, 204}, - {0x98165AF37B2153DF, 641, 212}, {0xE2A0B5DC971F303A, 667, 220}, - {0xA8D9D1535CE3B396, 694, 228}, {0xFB9B7CD9A4A7443C, 720, 236}, - {0xBB764C4CA7A44410, 747, 244}, {0x8BAB8EEFB6409C1A, 774, 252}, - {0xD01FEF10A657842C, 800, 260}, {0x9B10A4E5E9913129, 827, 268}, - {0xE7109BFBA19C0C9D, 853, 276}, {0xAC2820D9623BF429, 880, 284}, - {0x80444B5E7AA7CF85, 907, 292}, {0xBF21E44003ACDD2D, 933, 300}, - {0x8E679C2F5E44FF8F, 960, 308}, {0xD433179D9C8CB841, 986, 316}, - {0x9E19DB92B4E31BA9, 1013, 324}, - } }; + static constexpr std::array kCachedPowers = {{ + {0xAB70FE17C79AC6CA, -1060, -300}, {0xFF77B1FCBEBCDC4F, -1034, -292}, + {0xBE5691EF416BD60C, -1007, -284}, {0x8DD01FAD907FFC3C, -980, -276}, + {0xD3515C2831559A83, -954, -268}, {0x9D71AC8FADA6C9B5, -927, -260}, + {0xEA9C227723EE8BCB, -901, -252}, {0xAECC49914078536D, -874, -244}, + {0x823C12795DB6CE57, -847, -236}, {0xC21094364DFB5637, -821, -228}, + {0x9096EA6F3848984F, -794, -220}, {0xD77485CB25823AC7, -768, -212}, + {0xA086CFCD97BF97F4, -741, -204}, {0xEF340A98172AACE5, -715, -196}, + {0xB23867FB2A35B28E, -688, -188}, {0x84C8D4DFD2C63F3B, -661, -180}, + {0xC5DD44271AD3CDBA, -635, -172}, {0x936B9FCEBB25C996, -608, -164}, + {0xDBAC6C247D62A584, -582, -156}, {0xA3AB66580D5FDAF6, -555, -148}, + {0xF3E2F893DEC3F126, -529, -140}, {0xB5B5ADA8AAFF80B8, -502, -132}, + {0x87625F056C7C4A8B, -475, -124}, {0xC9BCFF6034C13053, -449, -116}, + {0x964E858C91BA2655, -422, -108}, {0xDFF9772470297EBD, -396, -100}, + {0xA6DFBD9FB8E5B88F, -369, -92}, {0xF8A95FCF88747D94, -343, -84}, + {0xB94470938FA89BCF, -316, -76}, {0x8A08F0F8BF0F156B, -289, -68}, + {0xCDB02555653131B6, -263, -60}, {0x993FE2C6D07B7FAC, -236, -52}, + {0xE45C10C42A2B3B06, -210, -44}, {0xAA242499697392D3, -183, -36}, + {0xFD87B5F28300CA0E, -157, -28}, {0xBCE5086492111AEB, -130, -20}, + {0x8CBCCC096F5088CC, -103, -12}, {0xD1B71758E219652C, -77, -4}, + {0x9C40000000000000, -50, 4}, {0xE8D4A51000000000, -24, 12}, + {0xAD78EBC5AC620000, 3, 20}, {0x813F3978F8940984, 30, 28}, + {0xC097CE7BC90715B3, 56, 36}, {0x8F7E32CE7BEA5C70, 83, 44}, + {0xD5D238A4ABE98068, 109, 52}, {0x9F4F2726179A2245, 136, 60}, + {0xED63A231D4C4FB27, 162, 68}, {0xB0DE65388CC8ADA8, 189, 76}, + {0x83C7088E1AAB65DB, 216, 84}, {0xC45D1DF942711D9A, 242, 92}, + {0x924D692CA61BE758, 269, 100}, {0xDA01EE641A708DEA, 295, 108}, + {0xA26DA3999AEF774A, 322, 116}, {0xF209787BB47D6B85, 348, 124}, + {0xB454E4A179DD1877, 375, 132}, {0x865B86925B9BC5C2, 402, 140}, + {0xC83553C5C8965D3D, 428, 148}, {0x952AB45CFA97A0B3, 455, 156}, + {0xDE469FBD99A05FE3, 481, 164}, {0xA59BC234DB398C25, 508, 172}, + {0xF6C69A72A3989F5C, 534, 180}, {0xB7DCBF5354E9BECE, 561, 188}, + {0x88FCF317F22241E2, 588, 196}, {0xCC20CE9BD35C78A5, 614, 204}, + {0x98165AF37B2153DF, 641, 212}, {0xE2A0B5DC971F303A, 667, 220}, + {0xA8D9D1535CE3B396, 694, 228}, {0xFB9B7CD9A4A7443C, 720, 236}, + {0xBB764C4CA7A44410, 747, 244}, {0x8BAB8EEFB6409C1A, 774, 252}, + {0xD01FEF10A657842C, 800, 260}, {0x9B10A4E5E9913129, 827, 268}, + {0xE7109BFBA19C0C9D, 853, 276}, {0xAC2820D9623BF429, 880, 284}, + {0x80444B5E7AA7CF85, 907, 292}, {0xBF21E44003ACDD2D, 933, 300}, + {0x8E679C2F5E44FF8F, 960, 308}, {0xD433179D9C8CB841, 986, 316}, + {0x9E19DB92B4E31BA9, 1013, 324}, + }}; - // This computation gives exactly the same results for k as - // k = ceil((kAlpha - e - 1) * 0.30102999566398114) - // for |e| <= 1500, but doesn't require floating-point operations. - // NB: log_10(2) ~= 78913 / 2^18 - const int f = kAlpha - e - 1; - const int k = (f * 78913) / (1 << 18) + static_cast(f > 0); + // This computation gives exactly the same results for k as + // k = ceil((kAlpha - e - 1) * 0.30102999566398114) + // for |e| <= 1500, but doesn't require floating-point operations. + // NB: log_10(2) ~= 78913 / 2^18 + const int f = kAlpha - e - 1; + const int k = (f * 78913) / (1 << 18) + static_cast(f > 0); - const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / + const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep; - const cached_power cached = kCachedPowers[static_cast(index)]; + const cached_power cached = kCachedPowers[static_cast(index)]; - return cached; - } + return cached; +} - /*! - For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k. - For n == 0, returns 1 and sets pow10 := 1. - */ - inline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10) { - // LCOV_EXCL_START - if (n >= 1000000000) { - pow10 = 1000000000; - return 10; - } - // LCOV_EXCL_STOP - else if (n >= 100000000) { - pow10 = 100000000; - return 9; - } - else if (n >= 10000000) { - pow10 = 10000000; - return 8; - } - else if (n >= 1000000) { - pow10 = 1000000; - return 7; - } - else if (n >= 100000) { - pow10 = 100000; - return 6; - } - else if (n >= 10000) { - pow10 = 10000; - return 5; - } - else if (n >= 1000) { - pow10 = 1000; - return 4; - } - else if (n >= 100) { - pow10 = 100; - return 3; - } - else if (n >= 10) { - pow10 = 10; - return 2; - } - else { - pow10 = 1; - return 1; - } - } +/*! +For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k. +For n == 0, returns 1 and sets pow10 := 1. +*/ +inline int find_largest_pow10(const std::uint32_t n, std::uint32_t &pow10) { + // LCOV_EXCL_START + if (n >= 1000000000) { + pow10 = 1000000000; + return 10; + } + // LCOV_EXCL_STOP + else if (n >= 100000000) { + pow10 = 100000000; + return 9; + } else if (n >= 10000000) { + pow10 = 10000000; + return 8; + } else if (n >= 1000000) { + pow10 = 1000000; + return 7; + } else if (n >= 100000) { + pow10 = 100000; + return 6; + } else if (n >= 10000) { + pow10 = 10000; + return 5; + } else if (n >= 1000) { + pow10 = 1000; + return 4; + } else if (n >= 100) { + pow10 = 100; + return 3; + } else if (n >= 10) { + pow10 = 10; + return 2; + } else { + pow10 = 1; + return 1; + } +} - inline void grisu2_round(char* buf, int len, std::uint64_t dist, - std::uint64_t delta, std::uint64_t rest, - std::uint64_t ten_k) { +inline void grisu2_round(char *buf, int len, std::uint64_t dist, + std::uint64_t delta, std::uint64_t rest, + std::uint64_t ten_k) { - // <--------------------------- delta ----> - // <---- dist ---------> - // --------------[------------------+-------------------]-------------- - // M- w M+ - // - // ten_k - // <------> - // <---- rest ----> - // --------------[------------------+----+--------------]-------------- - // w V - // = buf * 10^k - // - // ten_k represents a unit-in-the-last-place in the decimal representation - // stored in buf. - // Decrement buf by ten_k while this takes buf closer to w. + // <--------------------------- delta ----> + // <---- dist ---------> + // --------------[------------------+-------------------]-------------- + // M- w M+ + // + // ten_k + // <------> + // <---- rest ----> + // --------------[------------------+----+--------------]-------------- + // w V + // = buf * 10^k + // + // ten_k represents a unit-in-the-last-place in the decimal representation + // stored in buf. + // Decrement buf by ten_k while this takes buf closer to w. - // The tests are written in this order to avoid overflow in unsigned - // integer arithmetic. + // The tests are written in this order to avoid overflow in unsigned + // integer arithmetic. - while (rest < dist && delta - rest >= ten_k && - (rest + ten_k < dist || dist - rest > rest + ten_k - dist)) { - buf[len - 1]--; - rest += ten_k; - } - } + while (rest < dist && delta - rest >= ten_k && + (rest + ten_k < dist || dist - rest > rest + ten_k - dist)) { + buf[len - 1]--; + rest += ten_k; + } +} - /*! - Generates V = buffer * 10^decimal_exponent, such that M- <= V <= M+. - M- and M+ must be normalized and share the same exponent -60 <= e <= -32. - */ - inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, - diyfp M_minus, diyfp w, diyfp M_plus) { - static_assert(kAlpha >= -60, "internal error"); - static_assert(kGamma <= -32, "internal error"); +/*! +Generates V = buffer * 10^decimal_exponent, such that M- <= V <= M+. +M- and M+ must be normalized and share the same exponent -60 <= e <= -32. +*/ +inline void grisu2_digit_gen(char *buffer, int &length, int &decimal_exponent, + diyfp M_minus, diyfp w, diyfp M_plus) { + static_assert(kAlpha >= -60, "internal error"); + static_assert(kGamma <= -32, "internal error"); - // Generates the digits (and the exponent) of a decimal floating-point - // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's - // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= - // gamma. - // - // <--------------------------- delta ----> - // <---- dist ---------> - // --------------[------------------+-------------------]-------------- - // M- w M+ - // - // Grisu2 generates the digits of M+ from left to right and stops as soon as - // V is in [M-,M+]. + // Generates the digits (and the exponent) of a decimal floating-point + // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's + // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= + // gamma. + // + // <--------------------------- delta ----> + // <---- dist ---------> + // --------------[------------------+-------------------]-------------- + // M- w M+ + // + // Grisu2 generates the digits of M+ from left to right and stops as soon as + // V is in [M-,M+]. - std::uint64_t delta = - diyfp::sub(M_plus, M_minus) - .f; // (significand of (M+ - M-), implicit exponent is e) - std::uint64_t dist = - diyfp::sub(M_plus, w) - .f; // (significand of (M+ - w ), implicit exponent is e) + std::uint64_t delta = + diyfp::sub(M_plus, M_minus) + .f; // (significand of (M+ - M-), implicit exponent is e) + std::uint64_t dist = + diyfp::sub(M_plus, w) + .f; // (significand of (M+ - w ), implicit exponent is e) - // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0): - // - // M+ = f * 2^e - // = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e - // = ((p1 ) * 2^-e + (p2 )) * 2^e - // = p1 + p2 * 2^e + // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0): + // + // M+ = f * 2^e + // = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e + // = ((p1 ) * 2^-e + (p2 )) * 2^e + // = p1 + p2 * 2^e - const diyfp one(std::uint64_t{ 1 } << -M_plus.e, M_plus.e); + const diyfp one(std::uint64_t{1} << -M_plus.e, M_plus.e); - auto p1 = static_cast( - M_plus.f >> - -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.) - std::uint64_t p2 = M_plus.f & (one.f - 1); // p2 = f mod 2^-e + auto p1 = static_cast( + M_plus.f >> + -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.) + std::uint64_t p2 = M_plus.f & (one.f - 1); // p2 = f mod 2^-e - // 1) - // - // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0] + // 1) + // + // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0] - std::uint32_t pow10; - const int k = find_largest_pow10(p1, pow10); + std::uint32_t pow10; + const int k = find_largest_pow10(p1, pow10); - // 10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1) - // - // p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1)) - // = (d[k-1] ) * 10^(k-1) + (p1 mod 10^(k-1)) - // - // M+ = p1 + p2 * 2^e - // = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1)) + p2 * 2^e - // = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e - // = d[k-1] * 10^(k-1) + ( rest) * 2^e - // - // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0) - // - // p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0] - // - // but stop as soon as - // - // rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e + // 10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1) + // + // p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1)) + // = (d[k-1] ) * 10^(k-1) + (p1 mod 10^(k-1)) + // + // M+ = p1 + p2 * 2^e + // = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1)) + p2 * 2^e + // = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e + // = d[k-1] * 10^(k-1) + ( rest) * 2^e + // + // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0) + // + // p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0] + // + // but stop as soon as + // + // rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e - int n = k; - while (n > 0) { - // Invariants: - // M+ = buffer * 10^n + (p1 + p2 * 2^e) (buffer = 0 for n = k) - // pow10 = 10^(n-1) <= p1 < 10^n - // - const std::uint32_t d = p1 / pow10; // d = p1 div 10^(n-1) - const std::uint32_t r = p1 % pow10; // r = p1 mod 10^(n-1) - // - // M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e - // = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e) - // - buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d - // - // M+ = buffer * 10^(n-1) + (r + p2 * 2^e) - // - p1 = r; - n--; - // - // M+ = buffer * 10^n + (p1 + p2 * 2^e) - // pow10 = 10^n - // + int n = k; + while (n > 0) { + // Invariants: + // M+ = buffer * 10^n + (p1 + p2 * 2^e) (buffer = 0 for n = k) + // pow10 = 10^(n-1) <= p1 < 10^n + // + const std::uint32_t d = p1 / pow10; // d = p1 div 10^(n-1) + const std::uint32_t r = p1 % pow10; // r = p1 mod 10^(n-1) + // + // M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e + // = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e) + // + buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d + // + // M+ = buffer * 10^(n-1) + (r + p2 * 2^e) + // + p1 = r; + n--; + // + // M+ = buffer * 10^n + (p1 + p2 * 2^e) + // pow10 = 10^n + // - // Now check if enough digits have been generated. - // Compute - // - // p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e - // - // Note: - // Since rest and delta share the same exponent e, it suffices to - // compare the significands. - const std::uint64_t rest = (std::uint64_t{ p1 } << -one.e) + p2; - if (rest <= delta) { - // V = buffer * 10^n, with M- <= V <= M+. + // Now check if enough digits have been generated. + // Compute + // + // p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e + // + // Note: + // Since rest and delta share the same exponent e, it suffices to + // compare the significands. + const std::uint64_t rest = (std::uint64_t{p1} << -one.e) + p2; + if (rest <= delta) { + // V = buffer * 10^n, with M- <= V <= M+. - decimal_exponent += n; + decimal_exponent += n; - // We may now just stop. But instead look if the buffer could be - // decremented to bring V closer to w. - // - // pow10 = 10^n is now 1 ulp in the decimal representation V. - // The rounding procedure works with diyfp's with an implicit - // exponent of e. - // - // 10^n = (10^n * 2^-e) * 2^e = ulp * 2^e - // - const std::uint64_t ten_n = std::uint64_t{ pow10 } << -one.e; - grisu2_round(buffer, length, dist, delta, rest, ten_n); + // We may now just stop. But instead look if the buffer could be + // decremented to bring V closer to w. + // + // pow10 = 10^n is now 1 ulp in the decimal representation V. + // The rounding procedure works with diyfp's with an implicit + // exponent of e. + // + // 10^n = (10^n * 2^-e) * 2^e = ulp * 2^e + // + const std::uint64_t ten_n = std::uint64_t{pow10} << -one.e; + grisu2_round(buffer, length, dist, delta, rest, ten_n); - return; - } + return; + } - pow10 /= 10; - // - // pow10 = 10^(n-1) <= p1 < 10^n - // Invariants restored. - } + pow10 /= 10; + // + // pow10 = 10^(n-1) <= p1 < 10^n + // Invariants restored. + } - // 2) - // - // The digits of the integral part have been generated: - // - // M+ = d[k-1]...d[1]d[0] + p2 * 2^e - // = buffer + p2 * 2^e - // - // Now generate the digits of the fractional part p2 * 2^e. - // - // Note: - // No decimal point is generated: the exponent is adjusted instead. - // - // p2 actually represents the fraction - // - // p2 * 2^e - // = p2 / 2^-e - // = d[-1] / 10^1 + d[-2] / 10^2 + ... - // - // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...) - // - // p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m - // + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...) - // - // using - // - // 10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e) - // = ( d) * 2^-e + ( r) - // - // or - // 10^m * p2 * 2^e = d + r * 2^e - // - // i.e. - // - // M+ = buffer + p2 * 2^e - // = buffer + 10^-m * (d + r * 2^e) - // = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e - // - // and stop as soon as 10^-m * r * 2^e <= delta * 2^e + // 2) + // + // The digits of the integral part have been generated: + // + // M+ = d[k-1]...d[1]d[0] + p2 * 2^e + // = buffer + p2 * 2^e + // + // Now generate the digits of the fractional part p2 * 2^e. + // + // Note: + // No decimal point is generated: the exponent is adjusted instead. + // + // p2 actually represents the fraction + // + // p2 * 2^e + // = p2 / 2^-e + // = d[-1] / 10^1 + d[-2] / 10^2 + ... + // + // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...) + // + // p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m + // + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...) + // + // using + // + // 10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e) + // = ( d) * 2^-e + ( r) + // + // or + // 10^m * p2 * 2^e = d + r * 2^e + // + // i.e. + // + // M+ = buffer + p2 * 2^e + // = buffer + 10^-m * (d + r * 2^e) + // = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e + // + // and stop as soon as 10^-m * r * 2^e <= delta * 2^e - int m = 0; - for (;;) { - // Invariant: - // M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) - // * 2^e - // = buffer * 10^-m + 10^-m * (p2 ) - // * 2^e = buffer * 10^-m + 10^-m * (1/10 * (10 * p2) ) * 2^e = - // buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + - // (10*p2 mod 2^-e)) * 2^e - // - p2 *= 10; - const std::uint64_t d = p2 >> -one.e; // d = (10 * p2) div 2^-e - const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e - // - // M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e - // = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e)) - // = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e - // - buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d - // - // M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e - // - p2 = r; - m++; - // - // M+ = buffer * 10^-m + 10^-m * p2 * 2^e - // Invariant restored. + int m = 0; + for (;;) { + // Invariant: + // M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) + // * 2^e + // = buffer * 10^-m + 10^-m * (p2 ) + // * 2^e = buffer * 10^-m + 10^-m * (1/10 * (10 * p2) ) * 2^e = + // buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + + // (10*p2 mod 2^-e)) * 2^e + // + p2 *= 10; + const std::uint64_t d = p2 >> -one.e; // d = (10 * p2) div 2^-e + const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e + // + // M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e)) + // = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e + // + buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d + // + // M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e + // + p2 = r; + m++; + // + // M+ = buffer * 10^-m + 10^-m * p2 * 2^e + // Invariant restored. - // Check if enough digits have been generated. - // - // 10^-m * p2 * 2^e <= delta * 2^e - // p2 * 2^e <= 10^m * delta * 2^e - // p2 <= 10^m * delta - delta *= 10; - dist *= 10; - if (p2 <= delta) { - break; - } - } + // Check if enough digits have been generated. + // + // 10^-m * p2 * 2^e <= delta * 2^e + // p2 * 2^e <= 10^m * delta * 2^e + // p2 <= 10^m * delta + delta *= 10; + dist *= 10; + if (p2 <= delta) { + break; + } + } - // V = buffer * 10^-m, with M- <= V <= M+. + // V = buffer * 10^-m, with M- <= V <= M+. - decimal_exponent -= m; + decimal_exponent -= m; - // 1 ulp in the decimal representation is now 10^-m. - // Since delta and dist are now scaled by 10^m, we need to do the - // same with ulp in order to keep the units in sync. - // - // 10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e - // - const std::uint64_t ten_m = one.f; - grisu2_round(buffer, length, dist, delta, p2, ten_m); + // 1 ulp in the decimal representation is now 10^-m. + // Since delta and dist are now scaled by 10^m, we need to do the + // same with ulp in order to keep the units in sync. + // + // 10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e + // + const std::uint64_t ten_m = one.f; + grisu2_round(buffer, length, dist, delta, p2, ten_m); - // By construction this algorithm generates the shortest possible decimal - // number (Loitsch, Theorem 6.2) which rounds back to w. - // For an input number of precision p, at least - // - // N = 1 + ceil(p * log_10(2)) - // - // decimal digits are sufficient to identify all binary floating-point - // numbers (Matula, "In-and-Out conversions"). - // This implies that the algorithm does not produce more than N decimal - // digits. - // - // N = 17 for p = 53 (IEEE double precision) - // N = 9 for p = 24 (IEEE single precision) - } + // By construction this algorithm generates the shortest possible decimal + // number (Loitsch, Theorem 6.2) which rounds back to w. + // For an input number of precision p, at least + // + // N = 1 + ceil(p * log_10(2)) + // + // decimal digits are sufficient to identify all binary floating-point + // numbers (Matula, "In-and-Out conversions"). + // This implies that the algorithm does not produce more than N decimal + // digits. + // + // N = 17 for p = 53 (IEEE double precision) + // N = 9 for p = 24 (IEEE single precision) +} - /*! - v = buf * 10^decimal_exponent - len is the length of the buffer (number of decimal digits) - The buffer must be large enough, i.e. >= max_digits10. - */ - inline void grisu2(char* buf, int& len, int& decimal_exponent, diyfp m_minus, - diyfp v, diyfp m_plus) { +/*! +v = buf * 10^decimal_exponent +len is the length of the buffer (number of decimal digits) +The buffer must be large enough, i.e. >= max_digits10. +*/ +inline void grisu2(char *buf, int &len, int &decimal_exponent, diyfp m_minus, + diyfp v, diyfp m_plus) { - // --------(-----------------------+-----------------------)-------- (A) - // m- v m+ - // - // --------------------(-----------+-----------------------)-------- (B) - // m- v m+ - // - // First scale v (and m- and m+) such that the exponent is in the range - // [alpha, gamma]. + // --------(-----------------------+-----------------------)-------- (A) + // m- v m+ + // + // --------------------(-----------+-----------------------)-------- (B) + // m- v m+ + // + // First scale v (and m- and m+) such that the exponent is in the range + // [alpha, gamma]. - const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e); + const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e); - const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k + const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k - // The exponent of the products is = v.e + c_minus_k.e + q and is in the range - // [alpha,gamma] - const diyfp w = diyfp::mul(v, c_minus_k); - const diyfp w_minus = diyfp::mul(m_minus, c_minus_k); - const diyfp w_plus = diyfp::mul(m_plus, c_minus_k); + // The exponent of the products is = v.e + c_minus_k.e + q and is in the range + // [alpha,gamma] + const diyfp w = diyfp::mul(v, c_minus_k); + const diyfp w_minus = diyfp::mul(m_minus, c_minus_k); + const diyfp w_plus = diyfp::mul(m_plus, c_minus_k); - // ----(---+---)---------------(---+---)---------------(---+---)---- - // w- w w+ - // = c*m- = c*v = c*m+ - // - // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and - // w+ are now off by a small amount. - // In fact: - // - // w - v * 10^k < 1 ulp - // - // To account for this inaccuracy, add resp. subtract 1 ulp. - // - // --------+---[---------------(---+---)---------------]---+-------- - // w- M- w M+ w+ - // - // Now any number in [M-, M+] (bounds included) will round to w when input, - // regardless of how the input rounding algorithm breaks ties. - // - // And digit_gen generates the shortest possible such number in [M-, M+]. - // Note that this does not mean that Grisu2 always generates the shortest - // possible number in the interval (m-, m+). - const diyfp M_minus(w_minus.f + 1, w_minus.e); - const diyfp M_plus(w_plus.f - 1, w_plus.e); + // ----(---+---)---------------(---+---)---------------(---+---)---- + // w- w w+ + // = c*m- = c*v = c*m+ + // + // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and + // w+ are now off by a small amount. + // In fact: + // + // w - v * 10^k < 1 ulp + // + // To account for this inaccuracy, add resp. subtract 1 ulp. + // + // --------+---[---------------(---+---)---------------]---+-------- + // w- M- w M+ w+ + // + // Now any number in [M-, M+] (bounds included) will round to w when input, + // regardless of how the input rounding algorithm breaks ties. + // + // And digit_gen generates the shortest possible such number in [M-, M+]. + // Note that this does not mean that Grisu2 always generates the shortest + // possible number in the interval (m-, m+). + const diyfp M_minus(w_minus.f + 1, w_minus.e); + const diyfp M_plus(w_plus.f - 1, w_plus.e); - decimal_exponent = -cached.k; // = -(-k) = k + decimal_exponent = -cached.k; // = -(-k) = k - grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus); - } + grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus); +} - /*! - v = buf * 10^decimal_exponent - len is the length of the buffer (number of decimal digits) - The buffer must be large enough, i.e. >= max_digits10. - */ - template - void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value) { - static_assert(diyfp::kPrecision >= std::numeric_limits::digits + 3, - "internal error: not enough precision"); +/*! +v = buf * 10^decimal_exponent +len is the length of the buffer (number of decimal digits) +The buffer must be large enough, i.e. >= max_digits10. +*/ +template +void grisu2(char *buf, int &len, int &decimal_exponent, FloatType value) { + static_assert(diyfp::kPrecision >= std::numeric_limits::digits + 3, + "internal error: not enough precision"); - // If the neighbors (and boundaries) of 'value' are always computed for - // double-precision numbers, all float's can be recovered using strtod (and - // strtof). However, the resulting decimal representations are not exactly - // "short". - // - // The documentation for 'std::to_chars' - // (https://en.cppreference.com/w/cpp/utility/to_chars) says "value is - // converted to a string as if by std::sprintf in the default ("C") locale" - // and since sprintf promotes float's to double's, I think this is exactly - // what 'std::to_chars' does. On the other hand, the documentation for - // 'std::to_chars' requires that "parsing the representation using the - // corresponding std::from_chars function recovers value exactly". That - // indicates that single precision floating-point numbers should be recovered - // using 'std::strtof'. - // - // NB: If the neighbors are computed for single-precision numbers, there is a - // single float - // (7.0385307e-26f) which can't be recovered using strtod. The resulting - // double precision value is off by 1 ulp. + // If the neighbors (and boundaries) of 'value' are always computed for + // double-precision numbers, all float's can be recovered using strtod (and + // strtof). However, the resulting decimal representations are not exactly + // "short". + // + // The documentation for 'std::to_chars' + // (https://en.cppreference.com/w/cpp/utility/to_chars) says "value is + // converted to a string as if by std::sprintf in the default ("C") locale" + // and since sprintf promotes float's to double's, I think this is exactly + // what 'std::to_chars' does. On the other hand, the documentation for + // 'std::to_chars' requires that "parsing the representation using the + // corresponding std::from_chars function recovers value exactly". That + // indicates that single precision floating-point numbers should be recovered + // using 'std::strtof'. + // + // NB: If the neighbors are computed for single-precision numbers, there is a + // single float + // (7.0385307e-26f) which can't be recovered using strtod. The resulting + // double precision value is off by 1 ulp. #if 0 - const boundaries w = compute_boundaries(static_cast(value)); + const boundaries w = compute_boundaries(static_cast(value)); #else - const boundaries w = compute_boundaries(value); + const boundaries w = compute_boundaries(value); #endif - grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus); - } + grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus); +} - /*! - @brief appends a decimal representation of e to buf - @return a pointer to the element following the exponent. - @pre -1000 < e < 1000 - */ - inline char* append_exponent(char* buf, int e) { +/*! +@brief appends a decimal representation of e to buf +@return a pointer to the element following the exponent. +@pre -1000 < e < 1000 +*/ +inline char *append_exponent(char *buf, int e) { - if (e < 0) { - e = -e; - *buf++ = '-'; - } - else { - *buf++ = '+'; - } + bool isNegative = e < 0; + e = isNegative ? -e : e; + *buf++ = isNegative ? '-' : '+'; - auto k = static_cast(e); - if (k < 10) { - // Always print at least two digits in the exponent. - // This is for compatibility with printf("%g"). - *buf++ = '0'; - *buf++ = static_cast('0' + k); - } - else if (k < 100) { - *buf++ = static_cast('0' + k / 10); - k %= 10; - *buf++ = static_cast('0' + k); - } - else { - *buf++ = static_cast('0' + k / 100); - k %= 100; - *buf++ = static_cast('0' + k / 10); - k %= 10; - *buf++ = static_cast('0' + k); - } + auto k = static_cast(e); + if (k < 100) { + // Always print at least two digits in the exponent. + // This is for compatibility with printf("%g"). + *buf++ = static_cast('0' + k / 10); + k %= 10; + *buf++ = static_cast('0' + k); + } else { + *buf++ = static_cast('0' + k / 100); + k %= 100; + *buf++ = static_cast('0' + k / 10); + k %= 10; + *buf++ = static_cast('0' + k); + } - return buf; - } + return buf; +} - /*! - @brief prettify v = buf * 10^decimal_exponent - If v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point - notation. Otherwise it will be printed in exponential notation. - @pre min_exp < 0 - @pre max_exp > 0 - */ - inline char* format_buffer(char* buf, int len, int decimal_exponent, - int min_exp, int max_exp) { +/*! +@brief prettify v = buf * 10^decimal_exponent +If v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point +notation. Otherwise it will be printed in exponential notation. +@pre min_exp < 0 +@pre max_exp > 0 +*/ +inline char *format_buffer(char *buf, int len, int decimal_exponent, + int min_exp, int max_exp) { - const int k = len; - const int n = len + decimal_exponent; + const int k = len; + const int n = len + decimal_exponent; - // v = buf * 10^(n-k) - // k is the length of the buffer (number of decimal digits) - // n is the position of the decimal point relative to the start of the buffer. + // v = buf * 10^(n-k) + // k is the length of the buffer (number of decimal digits) + // n is the position of the decimal point relative to the start of the buffer. - if (k <= n && n <= max_exp) { - // digits[000] - // len <= max_exp + 2 + if (k <= n && n <= max_exp) { + // digits[000] + // len <= max_exp + 2 - std::memset(buf + k, '0', static_cast(n) - static_cast(k)); - // Make it look like a floating-point number (#362, #378) - buf[n + 0] = '.'; - buf[n + 1] = '0'; - return buf + (static_cast(n)) + 2; - } + std::memset(buf + k, '0', static_cast(n) - static_cast(k)); + // Make it look like a floating-point number (#362, #378) + buf[n + 0] = '.'; + buf[n + 1] = '0'; + return buf + (static_cast(n)) + 2; + } - if (0 < n && n <= max_exp) { - // dig.its - // len <= max_digits10 + 1 - std::memmove(buf + (static_cast(n) + 1), buf + n, - static_cast(k) - static_cast(n)); - buf[n] = '.'; - return buf + (static_cast(k) + 1U); - } + if (0 < n && n <= max_exp) { + // dig.its + // len <= max_digits10 + 1 + std::memmove(buf + (static_cast(n) + 1), buf + n, + static_cast(k) - static_cast(n)); + buf[n] = '.'; + return buf + (static_cast(k) + 1U); + } - if (min_exp < n && n <= 0) { - // 0.[000]digits - // len <= 2 + (-min_exp - 1) + max_digits10 + if (min_exp < n && n <= 0) { + // 0.[000]digits + // len <= 2 + (-min_exp - 1) + max_digits10 - std::memmove(buf + (2 + static_cast(-n)), buf, - static_cast(k)); - buf[0] = '0'; - buf[1] = '.'; - std::memset(buf + 2, '0', static_cast(-n)); - return buf + (2U + static_cast(-n) + static_cast(k)); - } + std::memmove(buf + (2 + static_cast(-n)), buf, + static_cast(k)); + buf[0] = '0'; + buf[1] = '.'; + std::memset(buf + 2, '0', static_cast(-n)); + return buf + (2U + static_cast(-n) + static_cast(k)); + } - if (k == 1) { - // dE+123 - // len <= 1 + 5 + if (k == 1) { + // dE+123 + // len <= 1 + 5 - buf += 1; - } - else { - // d.igitsE+123 - // len <= max_digits10 + 1 + 5 + buf += 1; + } else { + // d.igitsE+123 + // len <= max_digits10 + 1 + 5 - std::memmove(buf + 2, buf + 1, static_cast(k) - 1); - buf[1] = '.'; - buf += 1 + static_cast(k); - } + std::memmove(buf + 2, buf + 1, static_cast(k) - 1); + buf[1] = '.'; + buf += 1 + static_cast(k); + } - *buf++ = 'e'; - return append_exponent(buf, n - 1); - } + *buf++ = 'e'; + return append_exponent(buf, n - 1); +} - } // namespace dtoa_impl +} // namespace dtoa_impl - /*! - The format of the resulting decimal representation is similar to printf's %g - format. Returns an iterator pointing past-the-end of the decimal representation. - @note The input number must be finite, i.e. NaN's and Inf's are not supported. - @note The buffer must be large enough. - @note The result is NOT null-terminated. - */ - char* to_chars(char* first, const char* last, double value) { - static_cast(last); // maybe unused - fix warning - bool negative = std::signbit(value); - if (negative) { - value = -value; - *first++ = '-'; - } +/*! +The format of the resulting decimal representation is similar to printf's %g +format. Returns an iterator pointing past-the-end of the decimal representation. +@note The input number must be finite, i.e. NaN's and Inf's are not supported. +@note The buffer must be large enough. +@note The result is NOT null-terminated. +*/ +char *to_chars(char *first, const char *last, double value) { + static_cast(last); // maybe unused - fix warning + bool negative = std::signbit(value); + if (negative) { + value = -value; + *first++ = '-'; + } - if (value == 0) // +-0 - { - *first++ = '0'; - // Make it look like a floating-point number (#362, #378) - *first++ = '.'; - *first++ = '0'; - return first; - } - // Compute v = buffer * 10^decimal_exponent. - // The decimal digits are stored in the buffer, which needs to be interpreted - // as an unsigned decimal integer. - // len is the length of the buffer, i.e. the number of decimal digits. - int len = 0; - int decimal_exponent = 0; - dtoa_impl::grisu2(first, len, decimal_exponent, value); - // Format the buffer like printf("%.*g", prec, value) - constexpr int kMinExp = -4; - constexpr int kMaxExp = std::numeric_limits::digits10; + if (value == 0) // +-0 + { + *first++ = '0'; + // Make it look like a floating-point number (#362, #378) + *first++ = '.'; + *first++ = '0'; + return first; + } + // Compute v = buffer * 10^decimal_exponent. + // The decimal digits are stored in the buffer, which needs to be interpreted + // as an unsigned decimal integer. + // len is the length of the buffer, i.e. the number of decimal digits. + int len = 0; + int decimal_exponent = 0; + dtoa_impl::grisu2(first, len, decimal_exponent, value); + // Format the buffer like printf("%.*g", prec, value) + constexpr int kMinExp = -4; + constexpr int kMaxExp = std::numeric_limits::digits10; - return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, - kMaxExp); - } - } // namespace internal + return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, + kMaxExp); +} +} // namespace internal } // namespace simdjson #endif // SIMDJSON_SRC_TO_CHARS_CPP @@ -3789,612 +4199,599 @@ namespace simdjson { #include namespace simdjson { - namespace internal { +namespace internal { - /** - * The code in the internal::from_chars function is meant to handle the floating-point number parsing - * when we have more than 19 digits in the decimal mantissa. This should only be seen - * in adversarial scenarios: we do not expect production systems to even produce - * such floating-point numbers. - * - * The parser is based on work by Nigel Tao (at https://github.com/google/wuffs/) - * who credits Ken Thompson for the design (via a reference to the Go source - * code). See - * https://github.com/google/wuffs/blob/aa46859ea40c72516deffa1b146121952d6dfd3b/internal/cgen/base/floatconv-submodule-data.c - * https://github.com/google/wuffs/blob/46cd8105f47ca07ae2ba8e6a7818ef9c0df6c152/internal/cgen/base/floatconv-submodule-code.c - * It is probably not very fast but it is a fallback that should almost never be - * called in real life. Google Wuffs is published under APL 2.0. - **/ +/** + * The code in the internal::from_chars function is meant to handle the floating-point number parsing + * when we have more than 19 digits in the decimal mantissa. This should only be seen + * in adversarial scenarios: we do not expect production systems to even produce + * such floating-point numbers. + * + * The parser is based on work by Nigel Tao (at https://github.com/google/wuffs/) + * who credits Ken Thompson for the design (via a reference to the Go source + * code). See + * https://github.com/google/wuffs/blob/aa46859ea40c72516deffa1b146121952d6dfd3b/internal/cgen/base/floatconv-submodule-data.c + * https://github.com/google/wuffs/blob/46cd8105f47ca07ae2ba8e6a7818ef9c0df6c152/internal/cgen/base/floatconv-submodule-code.c + * It is probably not very fast but it is a fallback that should almost never be + * called in real life. Google Wuffs is published under APL 2.0. + **/ - namespace { - constexpr uint32_t max_digits = 768; - constexpr int32_t decimal_point_range = 2047; - } // namespace +namespace { +constexpr uint32_t max_digits = 768; +constexpr int32_t decimal_point_range = 2047; +} // namespace - struct adjusted_mantissa { - uint64_t mantissa; - int power2; - adjusted_mantissa() : mantissa(0), power2(0) {} - }; +struct adjusted_mantissa { + uint64_t mantissa; + int power2; + adjusted_mantissa() : mantissa(0), power2(0) {} +}; - struct decimal { - uint32_t num_digits; - int32_t decimal_point; - bool negative; - bool truncated; - uint8_t digits[max_digits]; - }; +struct decimal { + uint32_t num_digits; + int32_t decimal_point; + bool negative; + bool truncated; + uint8_t digits[max_digits]; +}; - template struct binary_format { - static constexpr int mantissa_explicit_bits(); - static constexpr int minimum_exponent(); - static constexpr int infinite_power(); - static constexpr int sign_index(); - }; +template struct binary_format { + static constexpr int mantissa_explicit_bits(); + static constexpr int minimum_exponent(); + static constexpr int infinite_power(); + static constexpr int sign_index(); +}; - template <> constexpr int binary_format::mantissa_explicit_bits() { - return 52; - } +template <> constexpr int binary_format::mantissa_explicit_bits() { + return 52; +} - template <> constexpr int binary_format::minimum_exponent() { - return -1023; - } - template <> constexpr int binary_format::infinite_power() { - return 0x7FF; - } +template <> constexpr int binary_format::minimum_exponent() { + return -1023; +} +template <> constexpr int binary_format::infinite_power() { + return 0x7FF; +} - template <> constexpr int binary_format::sign_index() { return 63; } +template <> constexpr int binary_format::sign_index() { return 63; } - bool is_integer(char c) noexcept { return (c >= '0' && c <= '9'); } +bool is_integer(char c) noexcept { return (c >= '0' && c <= '9'); } - // This should always succeed since it follows a call to parse_number. - decimal parse_decimal(const char*& p) noexcept { - decimal answer; - answer.num_digits = 0; - answer.decimal_point = 0; - answer.truncated = false; - answer.negative = (*p == '-'); - if ((*p == '-') || (*p == '+')) { - ++p; - } +// This should always succeed since it follows a call to parse_number. +decimal parse_decimal(const char *&p) noexcept { + decimal answer; + answer.num_digits = 0; + answer.decimal_point = 0; + answer.truncated = false; + answer.negative = (*p == '-'); + if ((*p == '-') || (*p == '+')) { + ++p; + } - while (*p == '0') { - ++p; - } - while (is_integer(*p)) { - if (answer.num_digits < max_digits) { - answer.digits[answer.num_digits] = uint8_t(*p - '0'); - } - answer.num_digits++; - ++p; - } - if (*p == '.') { - ++p; - const char* first_after_period = p; - // if we have not yet encountered a zero, we have to skip it as well - if (answer.num_digits == 0) { - // skip zeros - while (*p == '0') { - ++p; - } - } - while (is_integer(*p)) { - if (answer.num_digits < max_digits) { - answer.digits[answer.num_digits] = uint8_t(*p - '0'); - } - answer.num_digits++; - ++p; - } - answer.decimal_point = int32_t(first_after_period - p); - } - if (answer.num_digits > 0) { - const char* preverse = p - 1; - int32_t trailing_zeros = 0; - while ((*preverse == '0') || (*preverse == '.')) { - if (*preverse == '0') { trailing_zeros++; }; - --preverse; - } - answer.decimal_point += int32_t(answer.num_digits); - answer.num_digits -= uint32_t(trailing_zeros); - } - if (answer.num_digits > max_digits) { - answer.num_digits = max_digits; - answer.truncated = true; - } - if (('e' == *p) || ('E' == *p)) { - ++p; - bool neg_exp = false; - if ('-' == *p) { - neg_exp = true; - ++p; - } - else if ('+' == *p) { - ++p; - } - int32_t exp_number = 0; // exponential part - while (is_integer(*p)) { - uint8_t digit = uint8_t(*p - '0'); - if (exp_number < 0x10000) { - exp_number = 10 * exp_number + digit; - } - ++p; - } - answer.decimal_point += (neg_exp ? -exp_number : exp_number); - } - return answer; - } + while (*p == '0') { + ++p; + } + while (is_integer(*p)) { + if (answer.num_digits < max_digits) { + answer.digits[answer.num_digits] = uint8_t(*p - '0'); + } + answer.num_digits++; + ++p; + } + if (*p == '.') { + ++p; + const char *first_after_period = p; + // if we have not yet encountered a zero, we have to skip it as well + if (answer.num_digits == 0) { + // skip zeros + while (*p == '0') { + ++p; + } + } + while (is_integer(*p)) { + if (answer.num_digits < max_digits) { + answer.digits[answer.num_digits] = uint8_t(*p - '0'); + } + answer.num_digits++; + ++p; + } + answer.decimal_point = int32_t(first_after_period - p); + } + if(answer.num_digits > 0) { + const char *preverse = p - 1; + int32_t trailing_zeros = 0; + while ((*preverse == '0') || (*preverse == '.')) { + if(*preverse == '0') { trailing_zeros++; }; + --preverse; + } + answer.decimal_point += int32_t(answer.num_digits); + answer.num_digits -= uint32_t(trailing_zeros); + } + if(answer.num_digits > max_digits ) { + answer.num_digits = max_digits; + answer.truncated = true; + } + if (('e' == *p) || ('E' == *p)) { + ++p; + bool neg_exp = false; + if ('-' == *p) { + neg_exp = true; + ++p; + } else if ('+' == *p) { + ++p; + } + int32_t exp_number = 0; // exponential part + while (is_integer(*p)) { + uint8_t digit = uint8_t(*p - '0'); + if (exp_number < 0x10000) { + exp_number = 10 * exp_number + digit; + } + ++p; + } + answer.decimal_point += (neg_exp ? -exp_number : exp_number); + } + return answer; +} - // This should always succeed since it follows a call to parse_number. - // Will not read at or beyond the "end" pointer. - decimal parse_decimal(const char*& p, const char* end) noexcept { - decimal answer; - answer.num_digits = 0; - answer.decimal_point = 0; - answer.truncated = false; - if (p == end) { return answer; } // should never happen - answer.negative = (*p == '-'); - if ((*p == '-') || (*p == '+')) { - ++p; - } +// This should always succeed since it follows a call to parse_number. +// Will not read at or beyond the "end" pointer. +decimal parse_decimal(const char *&p, const char * end) noexcept { + decimal answer; + answer.num_digits = 0; + answer.decimal_point = 0; + answer.truncated = false; + if(p == end) { return answer; } // should never happen + answer.negative = (*p == '-'); + if ((*p == '-') || (*p == '+')) { + ++p; + } - while ((p != end) && (*p == '0')) { - ++p; - } - while ((p != end) && is_integer(*p)) { - if (answer.num_digits < max_digits) { - answer.digits[answer.num_digits] = uint8_t(*p - '0'); - } - answer.num_digits++; - ++p; - } - if ((p != end) && (*p == '.')) { - ++p; - if (p == end) { return answer; } // should never happen - const char* first_after_period = p; - // if we have not yet encountered a zero, we have to skip it as well - if (answer.num_digits == 0) { - // skip zeros - while (*p == '0') { - ++p; - } - } - while ((p != end) && is_integer(*p)) { - if (answer.num_digits < max_digits) { - answer.digits[answer.num_digits] = uint8_t(*p - '0'); - } - answer.num_digits++; - ++p; - } - answer.decimal_point = int32_t(first_after_period - p); - } - if (answer.num_digits > 0) { - const char* preverse = p - 1; - int32_t trailing_zeros = 0; - while ((*preverse == '0') || (*preverse == '.')) { - if (*preverse == '0') { trailing_zeros++; }; - --preverse; - } - answer.decimal_point += int32_t(answer.num_digits); - answer.num_digits -= uint32_t(trailing_zeros); - } - if (answer.num_digits > max_digits) { - answer.num_digits = max_digits; - answer.truncated = true; - } - if ((p != end) && (('e' == *p) || ('E' == *p))) { - ++p; - if (p == end) { return answer; } // should never happen - bool neg_exp = false; - if ('-' == *p) { - neg_exp = true; - ++p; - } - else if ('+' == *p) { - ++p; - } - int32_t exp_number = 0; // exponential part - while ((p != end) && is_integer(*p)) { - uint8_t digit = uint8_t(*p - '0'); - if (exp_number < 0x10000) { - exp_number = 10 * exp_number + digit; - } - ++p; - } - answer.decimal_point += (neg_exp ? -exp_number : exp_number); - } - return answer; - } + while ((p != end) && (*p == '0')) { + ++p; + } + while ((p != end) && is_integer(*p)) { + if (answer.num_digits < max_digits) { + answer.digits[answer.num_digits] = uint8_t(*p - '0'); + } + answer.num_digits++; + ++p; + } + if ((p != end) && (*p == '.')) { + ++p; + if(p == end) { return answer; } // should never happen + const char *first_after_period = p; + // if we have not yet encountered a zero, we have to skip it as well + if (answer.num_digits == 0) { + // skip zeros + while (*p == '0') { + ++p; + } + } + while ((p != end) && is_integer(*p)) { + if (answer.num_digits < max_digits) { + answer.digits[answer.num_digits] = uint8_t(*p - '0'); + } + answer.num_digits++; + ++p; + } + answer.decimal_point = int32_t(first_after_period - p); + } + if(answer.num_digits > 0) { + const char *preverse = p - 1; + int32_t trailing_zeros = 0; + while ((*preverse == '0') || (*preverse == '.')) { + if(*preverse == '0') { trailing_zeros++; }; + --preverse; + } + answer.decimal_point += int32_t(answer.num_digits); + answer.num_digits -= uint32_t(trailing_zeros); + } + if(answer.num_digits > max_digits ) { + answer.num_digits = max_digits; + answer.truncated = true; + } + if ((p != end) && (('e' == *p) || ('E' == *p))) { + ++p; + if(p == end) { return answer; } // should never happen + bool neg_exp = false; + if ('-' == *p) { + neg_exp = true; + ++p; + } else if ('+' == *p) { + ++p; + } + int32_t exp_number = 0; // exponential part + while ((p != end) && is_integer(*p)) { + uint8_t digit = uint8_t(*p - '0'); + if (exp_number < 0x10000) { + exp_number = 10 * exp_number + digit; + } + ++p; + } + answer.decimal_point += (neg_exp ? -exp_number : exp_number); + } + return answer; +} - namespace { +namespace { - // remove all final zeroes - inline void trim(decimal& h) { - while ((h.num_digits > 0) && (h.digits[h.num_digits - 1] == 0)) { - h.num_digits--; - } - } +// remove all final zeroes +inline void trim(decimal &h) { + while ((h.num_digits > 0) && (h.digits[h.num_digits - 1] == 0)) { + h.num_digits--; + } +} - uint32_t number_of_digits_decimal_left_shift(decimal& h, uint32_t shift) { - shift &= 63; - const static uint16_t number_of_digits_decimal_left_shift_table[65] = { - 0x0000, 0x0800, 0x0801, 0x0803, 0x1006, 0x1009, 0x100D, 0x1812, 0x1817, - 0x181D, 0x2024, 0x202B, 0x2033, 0x203C, 0x2846, 0x2850, 0x285B, 0x3067, - 0x3073, 0x3080, 0x388E, 0x389C, 0x38AB, 0x38BB, 0x40CC, 0x40DD, 0x40EF, - 0x4902, 0x4915, 0x4929, 0x513E, 0x5153, 0x5169, 0x5180, 0x5998, 0x59B0, - 0x59C9, 0x61E3, 0x61FD, 0x6218, 0x6A34, 0x6A50, 0x6A6D, 0x6A8B, 0x72AA, - 0x72C9, 0x72E9, 0x7B0A, 0x7B2B, 0x7B4D, 0x8370, 0x8393, 0x83B7, 0x83DC, - 0x8C02, 0x8C28, 0x8C4F, 0x9477, 0x949F, 0x94C8, 0x9CF2, 0x051C, 0x051C, - 0x051C, 0x051C, - }; - uint32_t x_a = number_of_digits_decimal_left_shift_table[shift]; - uint32_t x_b = number_of_digits_decimal_left_shift_table[shift + 1]; - uint32_t num_new_digits = x_a >> 11; - uint32_t pow5_a = 0x7FF & x_a; - uint32_t pow5_b = 0x7FF & x_b; - const static uint8_t - number_of_digits_decimal_left_shift_table_powers_of_5[0x051C] = { - 5, 2, 5, 1, 2, 5, 6, 2, 5, 3, 1, 2, 5, 1, 5, 6, 2, 5, 7, 8, 1, 2, 5, - 3, 9, 0, 6, 2, 5, 1, 9, 5, 3, 1, 2, 5, 9, 7, 6, 5, 6, 2, 5, 4, 8, 8, - 2, 8, 1, 2, 5, 2, 4, 4, 1, 4, 0, 6, 2, 5, 1, 2, 2, 0, 7, 0, 3, 1, 2, - 5, 6, 1, 0, 3, 5, 1, 5, 6, 2, 5, 3, 0, 5, 1, 7, 5, 7, 8, 1, 2, 5, 1, - 5, 2, 5, 8, 7, 8, 9, 0, 6, 2, 5, 7, 6, 2, 9, 3, 9, 4, 5, 3, 1, 2, 5, - 3, 8, 1, 4, 6, 9, 7, 2, 6, 5, 6, 2, 5, 1, 9, 0, 7, 3, 4, 8, 6, 3, 2, - 8, 1, 2, 5, 9, 5, 3, 6, 7, 4, 3, 1, 6, 4, 0, 6, 2, 5, 4, 7, 6, 8, 3, - 7, 1, 5, 8, 2, 0, 3, 1, 2, 5, 2, 3, 8, 4, 1, 8, 5, 7, 9, 1, 0, 1, 5, - 6, 2, 5, 1, 1, 9, 2, 0, 9, 2, 8, 9, 5, 5, 0, 7, 8, 1, 2, 5, 5, 9, 6, - 0, 4, 6, 4, 4, 7, 7, 5, 3, 9, 0, 6, 2, 5, 2, 9, 8, 0, 2, 3, 2, 2, 3, - 8, 7, 6, 9, 5, 3, 1, 2, 5, 1, 4, 9, 0, 1, 1, 6, 1, 1, 9, 3, 8, 4, 7, - 6, 5, 6, 2, 5, 7, 4, 5, 0, 5, 8, 0, 5, 9, 6, 9, 2, 3, 8, 2, 8, 1, 2, - 5, 3, 7, 2, 5, 2, 9, 0, 2, 9, 8, 4, 6, 1, 9, 1, 4, 0, 6, 2, 5, 1, 8, - 6, 2, 6, 4, 5, 1, 4, 9, 2, 3, 0, 9, 5, 7, 0, 3, 1, 2, 5, 9, 3, 1, 3, - 2, 2, 5, 7, 4, 6, 1, 5, 4, 7, 8, 5, 1, 5, 6, 2, 5, 4, 6, 5, 6, 6, 1, - 2, 8, 7, 3, 0, 7, 7, 3, 9, 2, 5, 7, 8, 1, 2, 5, 2, 3, 2, 8, 3, 0, 6, - 4, 3, 6, 5, 3, 8, 6, 9, 6, 2, 8, 9, 0, 6, 2, 5, 1, 1, 6, 4, 1, 5, 3, - 2, 1, 8, 2, 6, 9, 3, 4, 8, 1, 4, 4, 5, 3, 1, 2, 5, 5, 8, 2, 0, 7, 6, - 6, 0, 9, 1, 3, 4, 6, 7, 4, 0, 7, 2, 2, 6, 5, 6, 2, 5, 2, 9, 1, 0, 3, - 8, 3, 0, 4, 5, 6, 7, 3, 3, 7, 0, 3, 6, 1, 3, 2, 8, 1, 2, 5, 1, 4, 5, - 5, 1, 9, 1, 5, 2, 2, 8, 3, 6, 6, 8, 5, 1, 8, 0, 6, 6, 4, 0, 6, 2, 5, - 7, 2, 7, 5, 9, 5, 7, 6, 1, 4, 1, 8, 3, 4, 2, 5, 9, 0, 3, 3, 2, 0, 3, - 1, 2, 5, 3, 6, 3, 7, 9, 7, 8, 8, 0, 7, 0, 9, 1, 7, 1, 2, 9, 5, 1, 6, - 6, 0, 1, 5, 6, 2, 5, 1, 8, 1, 8, 9, 8, 9, 4, 0, 3, 5, 4, 5, 8, 5, 6, - 4, 7, 5, 8, 3, 0, 0, 7, 8, 1, 2, 5, 9, 0, 9, 4, 9, 4, 7, 0, 1, 7, 7, - 2, 9, 2, 8, 2, 3, 7, 9, 1, 5, 0, 3, 9, 0, 6, 2, 5, 4, 5, 4, 7, 4, 7, - 3, 5, 0, 8, 8, 6, 4, 6, 4, 1, 1, 8, 9, 5, 7, 5, 1, 9, 5, 3, 1, 2, 5, - 2, 2, 7, 3, 7, 3, 6, 7, 5, 4, 4, 3, 2, 3, 2, 0, 5, 9, 4, 7, 8, 7, 5, - 9, 7, 6, 5, 6, 2, 5, 1, 1, 3, 6, 8, 6, 8, 3, 7, 7, 2, 1, 6, 1, 6, 0, - 2, 9, 7, 3, 9, 3, 7, 9, 8, 8, 2, 8, 1, 2, 5, 5, 6, 8, 4, 3, 4, 1, 8, - 8, 6, 0, 8, 0, 8, 0, 1, 4, 8, 6, 9, 6, 8, 9, 9, 4, 1, 4, 0, 6, 2, 5, - 2, 8, 4, 2, 1, 7, 0, 9, 4, 3, 0, 4, 0, 4, 0, 0, 7, 4, 3, 4, 8, 4, 4, - 9, 7, 0, 7, 0, 3, 1, 2, 5, 1, 4, 2, 1, 0, 8, 5, 4, 7, 1, 5, 2, 0, 2, - 0, 0, 3, 7, 1, 7, 4, 2, 2, 4, 8, 5, 3, 5, 1, 5, 6, 2, 5, 7, 1, 0, 5, - 4, 2, 7, 3, 5, 7, 6, 0, 1, 0, 0, 1, 8, 5, 8, 7, 1, 1, 2, 4, 2, 6, 7, - 5, 7, 8, 1, 2, 5, 3, 5, 5, 2, 7, 1, 3, 6, 7, 8, 8, 0, 0, 5, 0, 0, 9, - 2, 9, 3, 5, 5, 6, 2, 1, 3, 3, 7, 8, 9, 0, 6, 2, 5, 1, 7, 7, 6, 3, 5, - 6, 8, 3, 9, 4, 0, 0, 2, 5, 0, 4, 6, 4, 6, 7, 7, 8, 1, 0, 6, 6, 8, 9, - 4, 5, 3, 1, 2, 5, 8, 8, 8, 1, 7, 8, 4, 1, 9, 7, 0, 0, 1, 2, 5, 2, 3, - 2, 3, 3, 8, 9, 0, 5, 3, 3, 4, 4, 7, 2, 6, 5, 6, 2, 5, 4, 4, 4, 0, 8, - 9, 2, 0, 9, 8, 5, 0, 0, 6, 2, 6, 1, 6, 1, 6, 9, 4, 5, 2, 6, 6, 7, 2, - 3, 6, 3, 2, 8, 1, 2, 5, 2, 2, 2, 0, 4, 4, 6, 0, 4, 9, 2, 5, 0, 3, 1, - 3, 0, 8, 0, 8, 4, 7, 2, 6, 3, 3, 3, 6, 1, 8, 1, 6, 4, 0, 6, 2, 5, 1, - 1, 1, 0, 2, 2, 3, 0, 2, 4, 6, 2, 5, 1, 5, 6, 5, 4, 0, 4, 2, 3, 6, 3, - 1, 6, 6, 8, 0, 9, 0, 8, 2, 0, 3, 1, 2, 5, 5, 5, 5, 1, 1, 1, 5, 1, 2, - 3, 1, 2, 5, 7, 8, 2, 7, 0, 2, 1, 1, 8, 1, 5, 8, 3, 4, 0, 4, 5, 4, 1, - 0, 1, 5, 6, 2, 5, 2, 7, 7, 5, 5, 5, 7, 5, 6, 1, 5, 6, 2, 8, 9, 1, 3, - 5, 1, 0, 5, 9, 0, 7, 9, 1, 7, 0, 2, 2, 7, 0, 5, 0, 7, 8, 1, 2, 5, 1, - 3, 8, 7, 7, 7, 8, 7, 8, 0, 7, 8, 1, 4, 4, 5, 6, 7, 5, 5, 2, 9, 5, 3, - 9, 5, 8, 5, 1, 1, 3, 5, 2, 5, 3, 9, 0, 6, 2, 5, 6, 9, 3, 8, 8, 9, 3, - 9, 0, 3, 9, 0, 7, 2, 2, 8, 3, 7, 7, 6, 4, 7, 6, 9, 7, 9, 2, 5, 5, 6, - 7, 6, 2, 6, 9, 5, 3, 1, 2, 5, 3, 4, 6, 9, 4, 4, 6, 9, 5, 1, 9, 5, 3, - 6, 1, 4, 1, 8, 8, 8, 2, 3, 8, 4, 8, 9, 6, 2, 7, 8, 3, 8, 1, 3, 4, 7, - 6, 5, 6, 2, 5, 1, 7, 3, 4, 7, 2, 3, 4, 7, 5, 9, 7, 6, 8, 0, 7, 0, 9, - 4, 4, 1, 1, 9, 2, 4, 4, 8, 1, 3, 9, 1, 9, 0, 6, 7, 3, 8, 2, 8, 1, 2, - 5, 8, 6, 7, 3, 6, 1, 7, 3, 7, 9, 8, 8, 4, 0, 3, 5, 4, 7, 2, 0, 5, 9, - 6, 2, 2, 4, 0, 6, 9, 5, 9, 5, 3, 3, 6, 9, 1, 4, 0, 6, 2, 5, - }; - const uint8_t* pow5 = - &number_of_digits_decimal_left_shift_table_powers_of_5[pow5_a]; - uint32_t i = 0; - uint32_t n = pow5_b - pow5_a; - for (; i < n; i++) { - if (i >= h.num_digits) { - return num_new_digits - 1; - } - else if (h.digits[i] == pow5[i]) { - continue; - } - else if (h.digits[i] < pow5[i]) { - return num_new_digits - 1; - } - else { - return num_new_digits; - } - } - return num_new_digits; - } +uint32_t number_of_digits_decimal_left_shift(decimal &h, uint32_t shift) { + shift &= 63; + const static uint16_t number_of_digits_decimal_left_shift_table[65] = { + 0x0000, 0x0800, 0x0801, 0x0803, 0x1006, 0x1009, 0x100D, 0x1812, 0x1817, + 0x181D, 0x2024, 0x202B, 0x2033, 0x203C, 0x2846, 0x2850, 0x285B, 0x3067, + 0x3073, 0x3080, 0x388E, 0x389C, 0x38AB, 0x38BB, 0x40CC, 0x40DD, 0x40EF, + 0x4902, 0x4915, 0x4929, 0x513E, 0x5153, 0x5169, 0x5180, 0x5998, 0x59B0, + 0x59C9, 0x61E3, 0x61FD, 0x6218, 0x6A34, 0x6A50, 0x6A6D, 0x6A8B, 0x72AA, + 0x72C9, 0x72E9, 0x7B0A, 0x7B2B, 0x7B4D, 0x8370, 0x8393, 0x83B7, 0x83DC, + 0x8C02, 0x8C28, 0x8C4F, 0x9477, 0x949F, 0x94C8, 0x9CF2, 0x051C, 0x051C, + 0x051C, 0x051C, + }; + uint32_t x_a = number_of_digits_decimal_left_shift_table[shift]; + uint32_t x_b = number_of_digits_decimal_left_shift_table[shift + 1]; + uint32_t num_new_digits = x_a >> 11; + uint32_t pow5_a = 0x7FF & x_a; + uint32_t pow5_b = 0x7FF & x_b; + const static uint8_t + number_of_digits_decimal_left_shift_table_powers_of_5[0x051C] = { + 5, 2, 5, 1, 2, 5, 6, 2, 5, 3, 1, 2, 5, 1, 5, 6, 2, 5, 7, 8, 1, 2, 5, + 3, 9, 0, 6, 2, 5, 1, 9, 5, 3, 1, 2, 5, 9, 7, 6, 5, 6, 2, 5, 4, 8, 8, + 2, 8, 1, 2, 5, 2, 4, 4, 1, 4, 0, 6, 2, 5, 1, 2, 2, 0, 7, 0, 3, 1, 2, + 5, 6, 1, 0, 3, 5, 1, 5, 6, 2, 5, 3, 0, 5, 1, 7, 5, 7, 8, 1, 2, 5, 1, + 5, 2, 5, 8, 7, 8, 9, 0, 6, 2, 5, 7, 6, 2, 9, 3, 9, 4, 5, 3, 1, 2, 5, + 3, 8, 1, 4, 6, 9, 7, 2, 6, 5, 6, 2, 5, 1, 9, 0, 7, 3, 4, 8, 6, 3, 2, + 8, 1, 2, 5, 9, 5, 3, 6, 7, 4, 3, 1, 6, 4, 0, 6, 2, 5, 4, 7, 6, 8, 3, + 7, 1, 5, 8, 2, 0, 3, 1, 2, 5, 2, 3, 8, 4, 1, 8, 5, 7, 9, 1, 0, 1, 5, + 6, 2, 5, 1, 1, 9, 2, 0, 9, 2, 8, 9, 5, 5, 0, 7, 8, 1, 2, 5, 5, 9, 6, + 0, 4, 6, 4, 4, 7, 7, 5, 3, 9, 0, 6, 2, 5, 2, 9, 8, 0, 2, 3, 2, 2, 3, + 8, 7, 6, 9, 5, 3, 1, 2, 5, 1, 4, 9, 0, 1, 1, 6, 1, 1, 9, 3, 8, 4, 7, + 6, 5, 6, 2, 5, 7, 4, 5, 0, 5, 8, 0, 5, 9, 6, 9, 2, 3, 8, 2, 8, 1, 2, + 5, 3, 7, 2, 5, 2, 9, 0, 2, 9, 8, 4, 6, 1, 9, 1, 4, 0, 6, 2, 5, 1, 8, + 6, 2, 6, 4, 5, 1, 4, 9, 2, 3, 0, 9, 5, 7, 0, 3, 1, 2, 5, 9, 3, 1, 3, + 2, 2, 5, 7, 4, 6, 1, 5, 4, 7, 8, 5, 1, 5, 6, 2, 5, 4, 6, 5, 6, 6, 1, + 2, 8, 7, 3, 0, 7, 7, 3, 9, 2, 5, 7, 8, 1, 2, 5, 2, 3, 2, 8, 3, 0, 6, + 4, 3, 6, 5, 3, 8, 6, 9, 6, 2, 8, 9, 0, 6, 2, 5, 1, 1, 6, 4, 1, 5, 3, + 2, 1, 8, 2, 6, 9, 3, 4, 8, 1, 4, 4, 5, 3, 1, 2, 5, 5, 8, 2, 0, 7, 6, + 6, 0, 9, 1, 3, 4, 6, 7, 4, 0, 7, 2, 2, 6, 5, 6, 2, 5, 2, 9, 1, 0, 3, + 8, 3, 0, 4, 5, 6, 7, 3, 3, 7, 0, 3, 6, 1, 3, 2, 8, 1, 2, 5, 1, 4, 5, + 5, 1, 9, 1, 5, 2, 2, 8, 3, 6, 6, 8, 5, 1, 8, 0, 6, 6, 4, 0, 6, 2, 5, + 7, 2, 7, 5, 9, 5, 7, 6, 1, 4, 1, 8, 3, 4, 2, 5, 9, 0, 3, 3, 2, 0, 3, + 1, 2, 5, 3, 6, 3, 7, 9, 7, 8, 8, 0, 7, 0, 9, 1, 7, 1, 2, 9, 5, 1, 6, + 6, 0, 1, 5, 6, 2, 5, 1, 8, 1, 8, 9, 8, 9, 4, 0, 3, 5, 4, 5, 8, 5, 6, + 4, 7, 5, 8, 3, 0, 0, 7, 8, 1, 2, 5, 9, 0, 9, 4, 9, 4, 7, 0, 1, 7, 7, + 2, 9, 2, 8, 2, 3, 7, 9, 1, 5, 0, 3, 9, 0, 6, 2, 5, 4, 5, 4, 7, 4, 7, + 3, 5, 0, 8, 8, 6, 4, 6, 4, 1, 1, 8, 9, 5, 7, 5, 1, 9, 5, 3, 1, 2, 5, + 2, 2, 7, 3, 7, 3, 6, 7, 5, 4, 4, 3, 2, 3, 2, 0, 5, 9, 4, 7, 8, 7, 5, + 9, 7, 6, 5, 6, 2, 5, 1, 1, 3, 6, 8, 6, 8, 3, 7, 7, 2, 1, 6, 1, 6, 0, + 2, 9, 7, 3, 9, 3, 7, 9, 8, 8, 2, 8, 1, 2, 5, 5, 6, 8, 4, 3, 4, 1, 8, + 8, 6, 0, 8, 0, 8, 0, 1, 4, 8, 6, 9, 6, 8, 9, 9, 4, 1, 4, 0, 6, 2, 5, + 2, 8, 4, 2, 1, 7, 0, 9, 4, 3, 0, 4, 0, 4, 0, 0, 7, 4, 3, 4, 8, 4, 4, + 9, 7, 0, 7, 0, 3, 1, 2, 5, 1, 4, 2, 1, 0, 8, 5, 4, 7, 1, 5, 2, 0, 2, + 0, 0, 3, 7, 1, 7, 4, 2, 2, 4, 8, 5, 3, 5, 1, 5, 6, 2, 5, 7, 1, 0, 5, + 4, 2, 7, 3, 5, 7, 6, 0, 1, 0, 0, 1, 8, 5, 8, 7, 1, 1, 2, 4, 2, 6, 7, + 5, 7, 8, 1, 2, 5, 3, 5, 5, 2, 7, 1, 3, 6, 7, 8, 8, 0, 0, 5, 0, 0, 9, + 2, 9, 3, 5, 5, 6, 2, 1, 3, 3, 7, 8, 9, 0, 6, 2, 5, 1, 7, 7, 6, 3, 5, + 6, 8, 3, 9, 4, 0, 0, 2, 5, 0, 4, 6, 4, 6, 7, 7, 8, 1, 0, 6, 6, 8, 9, + 4, 5, 3, 1, 2, 5, 8, 8, 8, 1, 7, 8, 4, 1, 9, 7, 0, 0, 1, 2, 5, 2, 3, + 2, 3, 3, 8, 9, 0, 5, 3, 3, 4, 4, 7, 2, 6, 5, 6, 2, 5, 4, 4, 4, 0, 8, + 9, 2, 0, 9, 8, 5, 0, 0, 6, 2, 6, 1, 6, 1, 6, 9, 4, 5, 2, 6, 6, 7, 2, + 3, 6, 3, 2, 8, 1, 2, 5, 2, 2, 2, 0, 4, 4, 6, 0, 4, 9, 2, 5, 0, 3, 1, + 3, 0, 8, 0, 8, 4, 7, 2, 6, 3, 3, 3, 6, 1, 8, 1, 6, 4, 0, 6, 2, 5, 1, + 1, 1, 0, 2, 2, 3, 0, 2, 4, 6, 2, 5, 1, 5, 6, 5, 4, 0, 4, 2, 3, 6, 3, + 1, 6, 6, 8, 0, 9, 0, 8, 2, 0, 3, 1, 2, 5, 5, 5, 5, 1, 1, 1, 5, 1, 2, + 3, 1, 2, 5, 7, 8, 2, 7, 0, 2, 1, 1, 8, 1, 5, 8, 3, 4, 0, 4, 5, 4, 1, + 0, 1, 5, 6, 2, 5, 2, 7, 7, 5, 5, 5, 7, 5, 6, 1, 5, 6, 2, 8, 9, 1, 3, + 5, 1, 0, 5, 9, 0, 7, 9, 1, 7, 0, 2, 2, 7, 0, 5, 0, 7, 8, 1, 2, 5, 1, + 3, 8, 7, 7, 7, 8, 7, 8, 0, 7, 8, 1, 4, 4, 5, 6, 7, 5, 5, 2, 9, 5, 3, + 9, 5, 8, 5, 1, 1, 3, 5, 2, 5, 3, 9, 0, 6, 2, 5, 6, 9, 3, 8, 8, 9, 3, + 9, 0, 3, 9, 0, 7, 2, 2, 8, 3, 7, 7, 6, 4, 7, 6, 9, 7, 9, 2, 5, 5, 6, + 7, 6, 2, 6, 9, 5, 3, 1, 2, 5, 3, 4, 6, 9, 4, 4, 6, 9, 5, 1, 9, 5, 3, + 6, 1, 4, 1, 8, 8, 8, 2, 3, 8, 4, 8, 9, 6, 2, 7, 8, 3, 8, 1, 3, 4, 7, + 6, 5, 6, 2, 5, 1, 7, 3, 4, 7, 2, 3, 4, 7, 5, 9, 7, 6, 8, 0, 7, 0, 9, + 4, 4, 1, 1, 9, 2, 4, 4, 8, 1, 3, 9, 1, 9, 0, 6, 7, 3, 8, 2, 8, 1, 2, + 5, 8, 6, 7, 3, 6, 1, 7, 3, 7, 9, 8, 8, 4, 0, 3, 5, 4, 7, 2, 0, 5, 9, + 6, 2, 2, 4, 0, 6, 9, 5, 9, 5, 3, 3, 6, 9, 1, 4, 0, 6, 2, 5, + }; + const uint8_t *pow5 = + &number_of_digits_decimal_left_shift_table_powers_of_5[pow5_a]; + uint32_t i = 0; + uint32_t n = pow5_b - pow5_a; + for (; i < n; i++) { + if (i >= h.num_digits) { + return num_new_digits - 1; + } else if (h.digits[i] == pow5[i]) { + continue; + } else if (h.digits[i] < pow5[i]) { + return num_new_digits - 1; + } else { + return num_new_digits; + } + } + return num_new_digits; +} - } // end of anonymous namespace +} // end of anonymous namespace - uint64_t round(decimal& h) { - if ((h.num_digits == 0) || (h.decimal_point < 0)) { - return 0; - } - else if (h.decimal_point > 18) { - return UINT64_MAX; - } - // at this point, we know that h.decimal_point >= 0 - uint32_t dp = uint32_t(h.decimal_point); - uint64_t n = 0; - for (uint32_t i = 0; i < dp; i++) { - n = (10 * n) + ((i < h.num_digits) ? h.digits[i] : 0); - } - bool round_up = false; - if (dp < h.num_digits) { - round_up = h.digits[dp] >= 5; // normally, we round up - // but we may need to round to even! - if ((h.digits[dp] == 5) && (dp + 1 == h.num_digits)) { - round_up = h.truncated || ((dp > 0) && (1 & h.digits[dp - 1])); - } - } - if (round_up) { - n++; - } - return n; - } +uint64_t round(decimal &h) { + if ((h.num_digits == 0) || (h.decimal_point < 0)) { + return 0; + } else if (h.decimal_point > 18) { + return UINT64_MAX; + } + // at this point, we know that h.decimal_point >= 0 + uint32_t dp = uint32_t(h.decimal_point); + uint64_t n = 0; + for (uint32_t i = 0; i < dp; i++) { + n = (10 * n) + ((i < h.num_digits) ? h.digits[i] : 0); + } + bool round_up = false; + if (dp < h.num_digits) { + round_up = h.digits[dp] >= 5; // normally, we round up + // but we may need to round to even! + if ((h.digits[dp] == 5) && (dp + 1 == h.num_digits)) { + round_up = h.truncated || ((dp > 0) && (1 & h.digits[dp - 1])); + } + } + if (round_up) { + n++; + } + return n; +} - // computes h * 2^-shift - void decimal_left_shift(decimal& h, uint32_t shift) { - if (h.num_digits == 0) { - return; - } - uint32_t num_new_digits = number_of_digits_decimal_left_shift(h, shift); - int32_t read_index = int32_t(h.num_digits - 1); - uint32_t write_index = h.num_digits - 1 + num_new_digits; - uint64_t n = 0; +// computes h * 2^-shift +void decimal_left_shift(decimal &h, uint32_t shift) { + if (h.num_digits == 0) { + return; + } + uint32_t num_new_digits = number_of_digits_decimal_left_shift(h, shift); + int32_t read_index = int32_t(h.num_digits - 1); + uint32_t write_index = h.num_digits - 1 + num_new_digits; + uint64_t n = 0; - while (read_index >= 0) { - n += uint64_t(h.digits[read_index]) << shift; - uint64_t quotient = n / 10; - uint64_t remainder = n - (10 * quotient); - if (write_index < max_digits) { - h.digits[write_index] = uint8_t(remainder); - } - else if (remainder > 0) { - h.truncated = true; - } - n = quotient; - write_index--; - read_index--; - } - while (n > 0) { - uint64_t quotient = n / 10; - uint64_t remainder = n - (10 * quotient); - if (write_index < max_digits) { - h.digits[write_index] = uint8_t(remainder); - } - else if (remainder > 0) { - h.truncated = true; - } - n = quotient; - write_index--; - } - h.num_digits += num_new_digits; - if (h.num_digits > max_digits) { - h.num_digits = max_digits; - } - h.decimal_point += int32_t(num_new_digits); - trim(h); - } + while (read_index >= 0) { + n += uint64_t(h.digits[read_index]) << shift; + uint64_t quotient = n / 10; + uint64_t remainder = n - (10 * quotient); + if (write_index < max_digits) { + h.digits[write_index] = uint8_t(remainder); + } else if (remainder > 0) { + h.truncated = true; + } + n = quotient; + write_index--; + read_index--; + } + while (n > 0) { + uint64_t quotient = n / 10; + uint64_t remainder = n - (10 * quotient); + if (write_index < max_digits) { + h.digits[write_index] = uint8_t(remainder); + } else if (remainder > 0) { + h.truncated = true; + } + n = quotient; + write_index--; + } + h.num_digits += num_new_digits; + if (h.num_digits > max_digits) { + h.num_digits = max_digits; + } + h.decimal_point += int32_t(num_new_digits); + trim(h); +} - // computes h * 2^shift - void decimal_right_shift(decimal& h, uint32_t shift) { - uint32_t read_index = 0; - uint32_t write_index = 0; +// computes h * 2^shift +void decimal_right_shift(decimal &h, uint32_t shift) { + uint32_t read_index = 0; + uint32_t write_index = 0; - uint64_t n = 0; + uint64_t n = 0; - while ((n >> shift) == 0) { - if (read_index < h.num_digits) { - n = (10 * n) + h.digits[read_index++]; - } - else if (n == 0) { - return; - } - else { - while ((n >> shift) == 0) { - n = 10 * n; - read_index++; - } - break; - } - } - h.decimal_point -= int32_t(read_index - 1); - if (h.decimal_point < -decimal_point_range) { // it is zero - h.num_digits = 0; - h.decimal_point = 0; - h.negative = false; - h.truncated = false; - return; - } - uint64_t mask = (uint64_t(1) << shift) - 1; - while (read_index < h.num_digits) { - uint8_t new_digit = uint8_t(n >> shift); - n = (10 * (n & mask)) + h.digits[read_index++]; - h.digits[write_index++] = new_digit; - } - while (n > 0) { - uint8_t new_digit = uint8_t(n >> shift); - n = 10 * (n & mask); - if (write_index < max_digits) { - h.digits[write_index++] = new_digit; - } - else if (new_digit > 0) { - h.truncated = true; - } - } - h.num_digits = write_index; - trim(h); - } + while ((n >> shift) == 0) { + if (read_index < h.num_digits) { + n = (10 * n) + h.digits[read_index++]; + } else if (n == 0) { + return; + } else { + while ((n >> shift) == 0) { + n = 10 * n; + read_index++; + } + break; + } + } + h.decimal_point -= int32_t(read_index - 1); + if (h.decimal_point < -decimal_point_range) { // it is zero + h.num_digits = 0; + h.decimal_point = 0; + h.negative = false; + h.truncated = false; + return; + } + uint64_t mask = (uint64_t(1) << shift) - 1; + while (read_index < h.num_digits) { + uint8_t new_digit = uint8_t(n >> shift); + n = (10 * (n & mask)) + h.digits[read_index++]; + h.digits[write_index++] = new_digit; + } + while (n > 0) { + uint8_t new_digit = uint8_t(n >> shift); + n = 10 * (n & mask); + if (write_index < max_digits) { + h.digits[write_index++] = new_digit; + } else if (new_digit > 0) { + h.truncated = true; + } + } + h.num_digits = write_index; + trim(h); +} - template adjusted_mantissa compute_float(decimal& d) { - adjusted_mantissa answer; - if (d.num_digits == 0) { - // should be zero - answer.power2 = 0; - answer.mantissa = 0; - return answer; - } - // At this point, going further, we can assume that d.num_digits > 0. - // We want to guard against excessive decimal point values because - // they can result in long running times. Indeed, we do - // shifts by at most 60 bits. We have that log(10**400)/log(2**60) ~= 22 - // which is fine, but log(10**299995)/log(2**60) ~= 16609 which is not - // fine (runs for a long time). - // - if (d.decimal_point < -324) { - // We have something smaller than 1e-324 which is always zero - // in binary64 and binary32. - // It should be zero. - answer.power2 = 0; - answer.mantissa = 0; - return answer; - } - else if (d.decimal_point >= 310) { - // We have something at least as large as 0.1e310 which is - // always infinite. - answer.power2 = binary::infinite_power(); - answer.mantissa = 0; - return answer; - } +template adjusted_mantissa compute_float(decimal &d) { + adjusted_mantissa answer; + if (d.num_digits == 0) { + // should be zero + answer.power2 = 0; + answer.mantissa = 0; + return answer; + } + // At this point, going further, we can assume that d.num_digits > 0. + // We want to guard against excessive decimal point values because + // they can result in long running times. Indeed, we do + // shifts by at most 60 bits. We have that log(10**400)/log(2**60) ~= 22 + // which is fine, but log(10**299995)/log(2**60) ~= 16609 which is not + // fine (runs for a long time). + // + if(d.decimal_point < -324) { + // We have something smaller than 1e-324 which is always zero + // in binary64 and binary32. + // It should be zero. + answer.power2 = 0; + answer.mantissa = 0; + return answer; + } else if(d.decimal_point >= 310) { + // We have something at least as large as 0.1e310 which is + // always infinite. + answer.power2 = binary::infinite_power(); + answer.mantissa = 0; + return answer; + } - static const uint32_t max_shift = 60; - static const uint32_t num_powers = 19; - static const uint8_t powers[19] = { - 0, 3, 6, 9, 13, 16, 19, 23, 26, 29, // - 33, 36, 39, 43, 46, 49, 53, 56, 59, // - }; - int32_t exp2 = 0; - while (d.decimal_point > 0) { - uint32_t n = uint32_t(d.decimal_point); - uint32_t shift = (n < num_powers) ? powers[n] : max_shift; - decimal_right_shift(d, shift); - if (d.decimal_point < -decimal_point_range) { - // should be zero - answer.power2 = 0; - answer.mantissa = 0; - return answer; - } - exp2 += int32_t(shift); - } - // We shift left toward [1/2 ... 1]. - while (d.decimal_point <= 0) { - uint32_t shift; - if (d.decimal_point == 0) { - if (d.digits[0] >= 5) { - break; - } - shift = (d.digits[0] < 2) ? 2 : 1; - } - else { - uint32_t n = uint32_t(-d.decimal_point); - shift = (n < num_powers) ? powers[n] : max_shift; - } - decimal_left_shift(d, shift); - if (d.decimal_point > decimal_point_range) { - // we want to get infinity: - answer.power2 = 0xFF; - answer.mantissa = 0; - return answer; - } - exp2 -= int32_t(shift); - } - // We are now in the range [1/2 ... 1] but the binary format uses [1 ... 2]. - exp2--; - constexpr int32_t minimum_exponent = binary::minimum_exponent(); - while ((minimum_exponent + 1) > exp2) { - uint32_t n = uint32_t((minimum_exponent + 1) - exp2); - if (n > max_shift) { - n = max_shift; - } - decimal_right_shift(d, n); - exp2 += int32_t(n); - } - if ((exp2 - minimum_exponent) >= binary::infinite_power()) { - answer.power2 = binary::infinite_power(); - answer.mantissa = 0; - return answer; - } + static const uint32_t max_shift = 60; + static const uint32_t num_powers = 19; + static const uint8_t powers[19] = { + 0, 3, 6, 9, 13, 16, 19, 23, 26, 29, // + 33, 36, 39, 43, 46, 49, 53, 56, 59, // + }; + int32_t exp2 = 0; + while (d.decimal_point > 0) { + uint32_t n = uint32_t(d.decimal_point); + uint32_t shift = (n < num_powers) ? powers[n] : max_shift; + decimal_right_shift(d, shift); + if (d.decimal_point < -decimal_point_range) { + // should be zero + answer.power2 = 0; + answer.mantissa = 0; + return answer; + } + exp2 += int32_t(shift); + } + // We shift left toward [1/2 ... 1]. + while (d.decimal_point <= 0) { + uint32_t shift; + if (d.decimal_point == 0) { + if (d.digits[0] >= 5) { + break; + } + shift = (d.digits[0] < 2) ? 2 : 1; + } else { + uint32_t n = uint32_t(-d.decimal_point); + shift = (n < num_powers) ? powers[n] : max_shift; + } + decimal_left_shift(d, shift); + if (d.decimal_point > decimal_point_range) { + // we want to get infinity: + answer.power2 = 0xFF; + answer.mantissa = 0; + return answer; + } + exp2 -= int32_t(shift); + } + // We are now in the range [1/2 ... 1] but the binary format uses [1 ... 2]. + exp2--; + constexpr int32_t minimum_exponent = binary::minimum_exponent(); + while ((minimum_exponent + 1) > exp2) { + uint32_t n = uint32_t((minimum_exponent + 1) - exp2); + if (n > max_shift) { + n = max_shift; + } + decimal_right_shift(d, n); + exp2 += int32_t(n); + } + if ((exp2 - minimum_exponent) >= binary::infinite_power()) { + answer.power2 = binary::infinite_power(); + answer.mantissa = 0; + return answer; + } - const int mantissa_size_in_bits = binary::mantissa_explicit_bits() + 1; - decimal_left_shift(d, mantissa_size_in_bits); + const int mantissa_size_in_bits = binary::mantissa_explicit_bits() + 1; + decimal_left_shift(d, mantissa_size_in_bits); - uint64_t mantissa = round(d); - // It is possible that we have an overflow, in which case we need - // to shift back. - if (mantissa >= (uint64_t(1) << mantissa_size_in_bits)) { - decimal_right_shift(d, 1); - exp2 += 1; - mantissa = round(d); - if ((exp2 - minimum_exponent) >= binary::infinite_power()) { - answer.power2 = binary::infinite_power(); - answer.mantissa = 0; - return answer; - } - } - answer.power2 = exp2 - binary::minimum_exponent(); - if (mantissa < (uint64_t(1) << binary::mantissa_explicit_bits())) { - answer.power2--; - } - answer.mantissa = - mantissa & ((uint64_t(1) << binary::mantissa_explicit_bits()) - 1); - return answer; - } + uint64_t mantissa = round(d); + // It is possible that we have an overflow, in which case we need + // to shift back. + if (mantissa >= (uint64_t(1) << mantissa_size_in_bits)) { + decimal_right_shift(d, 1); + exp2 += 1; + mantissa = round(d); + if ((exp2 - minimum_exponent) >= binary::infinite_power()) { + answer.power2 = binary::infinite_power(); + answer.mantissa = 0; + return answer; + } + } + answer.power2 = exp2 - binary::minimum_exponent(); + if (mantissa < (uint64_t(1) << binary::mantissa_explicit_bits())) { + answer.power2--; + } + answer.mantissa = + mantissa & ((uint64_t(1) << binary::mantissa_explicit_bits()) - 1); + return answer; +} - template - adjusted_mantissa parse_long_mantissa(const char* first) { - decimal d = parse_decimal(first); - return compute_float(d); - } +template +adjusted_mantissa parse_long_mantissa(const char *first) { + decimal d = parse_decimal(first); + return compute_float(d); +} - template - adjusted_mantissa parse_long_mantissa(const char* first, const char* end) { - decimal d = parse_decimal(first, end); - return compute_float(d); - } +template +adjusted_mantissa parse_long_mantissa(const char *first, const char *end) { + decimal d = parse_decimal(first, end); + return compute_float(d); +} - double from_chars(const char* first) noexcept { - bool negative = first[0] == '-'; - if (negative) { - first++; - } - adjusted_mantissa am = parse_long_mantissa>(first); - uint64_t word = am.mantissa; - word |= uint64_t(am.power2) - << binary_format::mantissa_explicit_bits(); - word = negative ? word | (uint64_t(1) << binary_format::sign_index()) - : word; - double value; - std::memcpy(&value, &word, sizeof(double)); - return value; - } +double from_chars(const char *first) noexcept { + bool negative = first[0] == '-'; + if (negative) { + first++; + } + adjusted_mantissa am = parse_long_mantissa>(first); + uint64_t word = am.mantissa; + word |= uint64_t(am.power2) + << binary_format::mantissa_explicit_bits(); + word = negative ? word | (uint64_t(1) << binary_format::sign_index()) + : word; + double value; + std::memcpy(&value, &word, sizeof(double)); + return value; +} - double from_chars(const char* first, const char* end) noexcept { - bool negative = first[0] == '-'; - if (negative) { - first++; - } - adjusted_mantissa am = parse_long_mantissa>(first, end); - uint64_t word = am.mantissa; - word |= uint64_t(am.power2) - << binary_format::mantissa_explicit_bits(); - word = negative ? word | (uint64_t(1) << binary_format::sign_index()) - : word; - double value; - std::memcpy(&value, &word, sizeof(double)); - return value; - } +double from_chars(const char *first, const char *end) noexcept { + bool negative = first[0] == '-'; + if (negative) { + first++; + } + adjusted_mantissa am = parse_long_mantissa>(first, end); + uint64_t word = am.mantissa; + word |= uint64_t(am.power2) + << binary_format::mantissa_explicit_bits(); + word = negative ? word | (uint64_t(1) << binary_format::sign_index()) + : word; + double value; + std::memcpy(&value, &word, sizeof(double)); + return value; +} - } // internal +} // internal } // simdjson #endif // SIMDJSON_SRC_FROM_CHARS_CPP @@ -4412,23 +4809,23 @@ namespace simdjson { /* 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); +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 +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]; +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 internal } // namespace simdjson #endif // SIMDJSON_INTERNAL_JSONCHARUTILS_TABLES_H @@ -4443,178 +4840,215 @@ namespace simdjson { #include namespace simdjson { - 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 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; - } +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 +// 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; - } +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); - } +inline std::ostream& operator<<(std::ostream& out, error_code error) noexcept { + return out << error_message(error); +} - namespace internal { +namespace internal { - // - // internal::simdjson_result_base inline implementation - // +// +// internal::simdjson_result_base inline implementation +// - template - simdjson_inline void simdjson_result_base::tie(T& value, error_code& error) && noexcept { - error = this->second; - if (!error) { - value = std::forward>(*this).first; - } - } +template +simdjson_inline void simdjson_result_base::tie(T &value, error_code &error) && noexcept { + error = this->second; + if (!error) { + value = std::forward>(*this).first; + } +} - template - simdjson_warn_unused simdjson_inline error_code simdjson_result_base::get(T& value) && noexcept { - error_code error; - std::forward>(*this).tie(value, error); - return error; - } +template +simdjson_warn_unused simdjson_inline error_code simdjson_result_base::get(T &value) && noexcept { + error_code error; + std::forward>(*this).tie(value, error); + return error; +} - template - simdjson_inline error_code simdjson_result_base::error() const noexcept { - return this->second; - } +template +simdjson_inline error_code simdjson_result_base::error() const noexcept { + return this->second; +} + + +template +simdjson_inline bool simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} #if SIMDJSON_EXCEPTIONS - template - simdjson_inline T& simdjson_result_base::value() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return this->first; - } - template - simdjson_inline T&& simdjson_result_base::value() && noexcept(false) { - return std::forward>(*this).take_value(); - } +template +simdjson_inline T& simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} - template - simdjson_inline T&& simdjson_result_base::take_value() && noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return std::forward(this->first); - } +template +simdjson_inline T&& simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} - template - simdjson_inline simdjson_result_base::operator T && () && noexcept(false) { - return std::forward>(*this).take_value(); - } +template +simdjson_inline T* simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + + +template +simdjson_inline const T* simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + +template +simdjson_inline T& simdjson_result_base::value() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return this->first; +} + +template +simdjson_inline T&& simdjson_result_base::value() && noexcept(false) { + return std::forward>(*this).take_value(); +} + +template +simdjson_inline T&& simdjson_result_base::take_value() && noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return std::forward(this->first); +} + +template +simdjson_inline simdjson_result_base::operator T&&() && noexcept(false) { + return std::forward>(*this).take_value(); +} #endif // SIMDJSON_EXCEPTIONS - template - simdjson_inline const T& simdjson_result_base::value_unsafe() const& noexcept { - return this->first; - } - template - simdjson_inline T&& simdjson_result_base::value_unsafe() && noexcept { - return std::forward(this->first); - } +template +simdjson_inline const T& simdjson_result_base::value_unsafe() const& noexcept { + return this->first; +} - template - simdjson_inline simdjson_result_base::simdjson_result_base(T&& value, error_code error) noexcept - : std::pair(std::forward(value), error) {} - template - simdjson_inline simdjson_result_base::simdjson_result_base(error_code error) noexcept - : simdjson_result_base(T{}, error) {} - template - simdjson_inline simdjson_result_base::simdjson_result_base(T&& value) noexcept - : simdjson_result_base(std::forward(value), SUCCESS) {} - template - simdjson_inline simdjson_result_base::simdjson_result_base() noexcept - : simdjson_result_base(T{}, UNINITIALIZED) {} +template +simdjson_inline T&& simdjson_result_base::value_unsafe() && noexcept { + return std::forward(this->first); +} - } // namespace internal +template +simdjson_inline simdjson_result_base::simdjson_result_base(T &&value, error_code error) noexcept + : std::pair(std::forward(value), error) {} +template +simdjson_inline simdjson_result_base::simdjson_result_base(error_code error) noexcept + : simdjson_result_base(T{}, error) {} +template +simdjson_inline simdjson_result_base::simdjson_result_base(T &&value) noexcept + : simdjson_result_base(std::forward(value), SUCCESS) {} +template +simdjson_inline simdjson_result_base::simdjson_result_base() noexcept + : simdjson_result_base(T{}, UNINITIALIZED) {} - /// - /// simdjson_result inline implementation - /// +} // namespace internal - template - simdjson_inline void simdjson_result::tie(T& value, error_code& error) && noexcept { - std::forward>(*this).tie(value, error); - } +/// +/// simdjson_result inline implementation +/// - template - simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T& value) && noexcept { - return std::forward>(*this).get(value); - } +template +simdjson_inline void simdjson_result::tie(T &value, error_code &error) && noexcept { + std::forward>(*this).tie(value, error); +} - template - simdjson_inline error_code simdjson_result::error() const noexcept { - return internal::simdjson_result_base::error(); - } +template +simdjson_warn_unused simdjson_inline error_code +simdjson_result::get(T &value) && noexcept { + return std::forward>(*this).get(value); +} + +template +simdjson_inline error_code simdjson_result::error() const noexcept { + return internal::simdjson_result_base::error(); +} #if SIMDJSON_EXCEPTIONS - template - simdjson_inline T& simdjson_result::value() & noexcept(false) { - return internal::simdjson_result_base::value(); - } +template +simdjson_inline T& simdjson_result::value() & noexcept(false) { + return internal::simdjson_result_base::value(); +} - template - simdjson_inline T&& simdjson_result::value() && noexcept(false) { - return std::forward>(*this).value(); - } +template +simdjson_inline T&& simdjson_result::value() && noexcept(false) { + return std::forward>(*this).value(); +} - template - simdjson_inline T&& simdjson_result::take_value() && noexcept(false) { - return std::forward>(*this).take_value(); - } +template +simdjson_inline T&& simdjson_result::take_value() && noexcept(false) { + return std::forward>(*this).take_value(); +} - template - simdjson_inline simdjson_result::operator T && () && noexcept(false) { - return std::forward>(*this).take_value(); - } +template +simdjson_inline simdjson_result::operator T&&() && noexcept(false) { + return std::forward>(*this).take_value(); +} #endif // SIMDJSON_EXCEPTIONS - template - simdjson_inline const T& simdjson_result::value_unsafe() const& noexcept { - return internal::simdjson_result_base::value_unsafe(); - } +template +simdjson_inline const T& simdjson_result::value_unsafe() const& noexcept { + return internal::simdjson_result_base::value_unsafe(); +} - template - simdjson_inline T&& simdjson_result::value_unsafe() && noexcept { - return std::forward>(*this).value_unsafe(); - } +template +simdjson_inline T&& simdjson_result::value_unsafe() && noexcept { + return std::forward>(*this).value_unsafe(); +} - template - simdjson_inline simdjson_result::simdjson_result(T&& value, error_code error) noexcept - : internal::simdjson_result_base(std::forward(value), error) {} - template - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : internal::simdjson_result_base(error) {} - template - simdjson_inline simdjson_result::simdjson_result(T&& value) noexcept - : internal::simdjson_result_base(std::forward(value)) {} - template - simdjson_inline simdjson_result::simdjson_result() noexcept - : internal::simdjson_result_base() {} +template +simdjson_inline simdjson_result::simdjson_result(T &&value, error_code error) noexcept + : internal::simdjson_result_base(std::forward(value), error) {} +template +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : internal::simdjson_result_base(error) {} +template +simdjson_inline simdjson_result::simdjson_result(T &&value) noexcept + : internal::simdjson_result_base(std::forward(value)) {} +template +simdjson_inline simdjson_result::simdjson_result() noexcept + : internal::simdjson_result_base() {} } // namespace simdjson @@ -4622,44 +5056,45 @@ namespace simdjson { /* end file simdjson/error-inl.h */ namespace simdjson { - namespace internal { +namespace internal { - SIMDJSON_DLLIMPORTEXPORT const error_code_info error_codes[]{ - { SUCCESS, "SUCCESS: No error" }, - { CAPACITY, "CAPACITY: This parser can't support a document that big" }, - { MEMALLOC, "MEMALLOC: Error allocating memory, we're most likely out of memory" }, - { TAPE_ERROR, "TAPE_ERROR: The JSON document has an improper structure: missing or superfluous commas, braces, missing keys, etc." }, - { DEPTH_ERROR, "DEPTH_ERROR: The JSON document was too deep (too many nested objects and arrays)" }, - { STRING_ERROR, "STRING_ERROR: Problem while parsing a string" }, - { T_ATOM_ERROR, "T_ATOM_ERROR: Problem while parsing an atom starting with the letter 't'" }, - { F_ATOM_ERROR, "F_ATOM_ERROR: Problem while parsing an atom starting with the letter 'f'" }, - { N_ATOM_ERROR, "N_ATOM_ERROR: Problem while parsing an atom starting with the letter 'n'" }, - { NUMBER_ERROR, "NUMBER_ERROR: Problem while parsing a number" }, - { BIGINT_ERROR, "BIGINT_ERROR: Big integer value that cannot be represented using 64 bits" }, - { UTF8_ERROR, "UTF8_ERROR: The input is not valid UTF-8" }, - { UNINITIALIZED, "UNINITIALIZED: Uninitialized" }, - { EMPTY, "EMPTY: no JSON found" }, - { UNESCAPED_CHARS, "UNESCAPED_CHARS: Within strings, some characters must be escaped, we found unescaped characters" }, - { UNCLOSED_STRING, "UNCLOSED_STRING: A string is opened, but never closed." }, - { UNSUPPORTED_ARCHITECTURE, "UNSUPPORTED_ARCHITECTURE: simdjson does not have an implementation supported by this CPU architecture. Please report this error to the core team as it should never happen." }, - { INCORRECT_TYPE, "INCORRECT_TYPE: The JSON element does not have the requested type." }, - { NUMBER_OUT_OF_RANGE, "NUMBER_OUT_OF_RANGE: The JSON number is too large or too small to fit within the requested type." }, - { INDEX_OUT_OF_BOUNDS, "INDEX_OUT_OF_BOUNDS: Attempted to access an element of a JSON array that is beyond its length." }, - { NO_SUCH_FIELD, "NO_SUCH_FIELD: The JSON field referenced does not exist in this object." }, - { IO_ERROR, "IO_ERROR: Error reading the file." }, - { INVALID_JSON_POINTER, "INVALID_JSON_POINTER: Invalid JSON pointer syntax." }, - { INVALID_URI_FRAGMENT, "INVALID_URI_FRAGMENT: Invalid URI fragment syntax." }, - { UNEXPECTED_ERROR, "UNEXPECTED_ERROR: Unexpected error, consider reporting this problem as you may have found a bug in simdjson" }, - { PARSER_IN_USE, "PARSER_IN_USE: Cannot parse a new document while a document is still in use." }, - { OUT_OF_ORDER_ITERATION, "OUT_OF_ORDER_ITERATION: Objects and arrays can only be iterated when they are first encountered." }, - { INSUFFICIENT_PADDING, "INSUFFICIENT_PADDING: simdjson requires the input JSON string to have at least SIMDJSON_PADDING extra bytes allocated, beyond the string's length. Consider using the simdjson::padded_string class if needed." }, - { INCOMPLETE_ARRAY_OR_OBJECT, "INCOMPLETE_ARRAY_OR_OBJECT: JSON document ended early in the middle of an object or array." }, - { SCALAR_DOCUMENT_AS_VALUE, "SCALAR_DOCUMENT_AS_VALUE: A JSON document made of a scalar (number, Boolean, null or string) is treated as a value. Use get_bool(), get_double(), etc. on the document instead. "}, - { OUT_OF_BOUNDS, "OUT_OF_BOUNDS: Attempt to access location outside of document."}, - { TRAILING_CONTENT, "TRAILING_CONTENT: Unexpected trailing content in the JSON input."} - }; // error_messages[] + SIMDJSON_DLLIMPORTEXPORT const error_code_info error_codes[] { + { SUCCESS, "SUCCESS: No error" }, + { CAPACITY, "CAPACITY: This parser can't support a document that big" }, + { MEMALLOC, "MEMALLOC: Error allocating memory, we're most likely out of memory" }, + { TAPE_ERROR, "TAPE_ERROR: The JSON document has an improper structure: missing or superfluous commas, braces, missing keys, etc. This is a fatal and unrecoverable error." }, + { DEPTH_ERROR, "DEPTH_ERROR: The JSON document was too deep (too many nested objects and arrays)" }, + { STRING_ERROR, "STRING_ERROR: Problem while parsing a string" }, + { T_ATOM_ERROR, "T_ATOM_ERROR: Problem while parsing an atom starting with the letter 't'" }, + { F_ATOM_ERROR, "F_ATOM_ERROR: Problem while parsing an atom starting with the letter 'f'" }, + { N_ATOM_ERROR, "N_ATOM_ERROR: Problem while parsing an atom starting with the letter 'n'" }, + { NUMBER_ERROR, "NUMBER_ERROR: Problem while parsing a number" }, + { BIGINT_ERROR, "BIGINT_ERROR: Big integer value that cannot be represented using 64 bits" }, + { UTF8_ERROR, "UTF8_ERROR: The input is not valid UTF-8" }, + { UNINITIALIZED, "UNINITIALIZED: Uninitialized" }, + { EMPTY, "EMPTY: no JSON found" }, + { UNESCAPED_CHARS, "UNESCAPED_CHARS: Within strings, some characters must be escaped, we found unescaped characters" }, + { UNCLOSED_STRING, "UNCLOSED_STRING: A string is opened, but never closed." }, + { UNSUPPORTED_ARCHITECTURE, "UNSUPPORTED_ARCHITECTURE: simdjson does not have an implementation supported by this CPU architecture. Please report this error to the core team as it should never happen." }, + { INCORRECT_TYPE, "INCORRECT_TYPE: The JSON element does not have the requested type." }, + { NUMBER_OUT_OF_RANGE, "NUMBER_OUT_OF_RANGE: The JSON number is too large or too small to fit within the requested type." }, + { INDEX_OUT_OF_BOUNDS, "INDEX_OUT_OF_BOUNDS: Attempted to access an element of a JSON array that is beyond its length." }, + { NO_SUCH_FIELD, "NO_SUCH_FIELD: The JSON field referenced does not exist in this object." }, + { IO_ERROR, "IO_ERROR: Error reading the file." }, + { INVALID_JSON_POINTER, "INVALID_JSON_POINTER: Invalid JSON pointer syntax." }, + { INVALID_URI_FRAGMENT, "INVALID_URI_FRAGMENT: Invalid URI fragment syntax." }, + { UNEXPECTED_ERROR, "UNEXPECTED_ERROR: Unexpected error, consider reporting this problem as you may have found a bug in simdjson" }, + { PARSER_IN_USE, "PARSER_IN_USE: Cannot parse a new document while a document is still in use." }, + { OUT_OF_ORDER_ITERATION, "OUT_OF_ORDER_ITERATION: Objects and arrays can only be iterated when they are first encountered." }, + { INSUFFICIENT_PADDING, "INSUFFICIENT_PADDING: simdjson requires the input JSON string to have at least SIMDJSON_PADDING extra bytes allocated, beyond the string's length. Consider using the simdjson::padded_string class if needed." }, + { INCOMPLETE_ARRAY_OR_OBJECT, "INCOMPLETE_ARRAY_OR_OBJECT: JSON document ended early in the middle of an object or array. This is a fatal and unrecoverable error." }, + { SCALAR_DOCUMENT_AS_VALUE, "SCALAR_DOCUMENT_AS_VALUE: A JSON document made of a scalar (number, Boolean, null or string) is treated as a value. Use get_bool(), get_double(), etc. on the document instead. "}, + { OUT_OF_BOUNDS, "OUT_OF_BOUNDS: Attempt to access location outside of document."}, + { TRAILING_CONTENT, "TRAILING_CONTENT: Unexpected trailing content in the JSON input."}, + { OUT_OF_CAPACITY, "OUT_OF_CAPACITY: The capacity was exceeded, we cannot allocate enough memory."} + }; // error_messages[] - } // namespace internal +} // namespace internal } // namespace simdjson #endif // SIMDJSON_SRC_ERROR_TABLES_CPP @@ -4672,194 +5107,194 @@ namespace simdjson { /* skipped duplicate #include */ namespace simdjson { - namespace internal { +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 +// 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 - SIMDJSON_DLLIMPORTEXPORT const bool structural_or_whitespace_negated[256] = { - 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, +SIMDJSON_DLLIMPORTEXPORT const bool structural_or_whitespace_negated[256] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 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, 1, 0, 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, 1, 0, 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, 1, 0, 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, 1, 0, 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, 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, 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, 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, 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, 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, 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, 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, 1, 1}; - SIMDJSON_DLLIMPORTEXPORT const bool structural_or_whitespace[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 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, 1, 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, 1, 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, 1, 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 }; +SIMDJSON_DLLIMPORTEXPORT const bool structural_or_whitespace[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 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, 1, 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, 1, 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, 1, 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}; - SIMDJSON_DLLIMPORTEXPORT const uint32_t digit_to_val32[886] = { - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, - 0x6, 0x7, 0x8, 0x9, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xa, - 0xb, 0xc, 0xd, 0xe, 0xf, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xa, 0xb, 0xc, 0xd, 0xe, - 0xf, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0x0, 0x10, 0x20, 0x30, 0x40, 0x50, - 0x60, 0x70, 0x80, 0x90, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xa0, - 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, - 0xf0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0x0, 0x100, 0x200, 0x300, 0x400, 0x500, - 0x600, 0x700, 0x800, 0x900, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xa00, - 0xb00, 0xc00, 0xd00, 0xe00, 0xf00, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xa00, 0xb00, 0xc00, 0xd00, 0xe00, - 0xf00, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0x0, 0x1000, 0x2000, 0x3000, 0x4000, 0x5000, - 0x6000, 0x7000, 0x8000, 0x9000, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xa000, - 0xb000, 0xc000, 0xd000, 0xe000, 0xf000, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xa000, 0xb000, 0xc000, 0xd000, 0xe000, - 0xf000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; +SIMDJSON_DLLIMPORTEXPORT const uint32_t digit_to_val32[886] = { + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, + 0x6, 0x7, 0x8, 0x9, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xa, + 0xb, 0xc, 0xd, 0xe, 0xf, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xa, 0xb, 0xc, 0xd, 0xe, + 0xf, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x0, 0x10, 0x20, 0x30, 0x40, 0x50, + 0x60, 0x70, 0x80, 0x90, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xa0, + 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, + 0xf0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x0, 0x100, 0x200, 0x300, 0x400, 0x500, + 0x600, 0x700, 0x800, 0x900, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xa00, + 0xb00, 0xc00, 0xd00, 0xe00, 0xf00, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xa00, 0xb00, 0xc00, 0xd00, 0xe00, + 0xf00, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x0, 0x1000, 0x2000, 0x3000, 0x4000, 0x5000, + 0x6000, 0x7000, 0x8000, 0x9000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xa000, + 0xb000, 0xc000, 0xd000, 0xe000, 0xf000, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xa000, 0xb000, 0xc000, 0xd000, 0xe000, + 0xf000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}; - } // namespace internal +} // namespace internal } // namespace simdjson #endif // SIMDJSON_SRC_JSONCHARUTILS_TABLES_CPP @@ -4878,66 +5313,38 @@ namespace simdjson { /* 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; +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; - }; +/** + * 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. - extern SIMDJSON_DLLIMPORTEXPORT const uint64_t power_of_five_128[]; - } // namespace internal -} // namespace simdjson - -#endif // SIMDJSON_INTERNAL_NUMBERPARSING_TABLES_H -/* end file simdjson/internal/numberparsing_tables.h */ - // Precomputed powers of ten from 10^0 to 10^22. These // can be represented exactly using the double type. -SIMDJSON_DLLIMPORTEXPORT const double simdjson::internal::power_of_ten[] = { - 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, - 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22 }; +extern SIMDJSON_DLLIMPORTEXPORT const double power_of_ten[]; + /** * When mapping numbers from decimal to binary, @@ -4951,10 +5358,13 @@ SIMDJSON_DLLIMPORTEXPORT const double simdjson::internal::power_of_ten[] = { */ - // 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. -SIMDJSON_DLLIMPORTEXPORT const uint64_t simdjson::internal::power_of_five_128[] = { +// 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 struct powers_template { +constexpr static uint64_t power_of_five_128[651*2]= { 0xeef453d6923bd65a,0x113faa2906a13b3f, 0x9558b4661b6565f8,0x4ac7ca59a424c507, 0xbaaee17fa23ebf76,0x5d79bcf00d2df649, @@ -5605,7 +6015,689 @@ SIMDJSON_DLLIMPORTEXPORT const uint64_t simdjson::internal::power_of_five_128[] 0x91d28b7416cdd27e,0x4cdc331d57fa5441, 0xb6472e511c81471d,0xe0133fe4adf8e952, 0xe3d8f9e563a198e5,0x58180fddd97723a6, - 0x8e679c2f5e44ff8f,0x570f09eaa7ea7648, }; + 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 */ + +// Precomputed powers of ten from 10^0 to 10^22. These +// can be represented exactly using the double type. +SIMDJSON_DLLIMPORTEXPORT const double simdjson::internal::power_of_ten[] = { + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, + 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22}; + +/** + * 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. +SIMDJSON_DLLIMPORTEXPORT const uint64_t simdjson::internal::power_of_five_128[]= { + 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_SRC_NUMBERPARSING_TABLES_CPP /* end file internal/numberparsing_tables.cpp */ @@ -5630,6 +6722,8 @@ SIMDJSON_DLLIMPORTEXPORT const uint64_t simdjson::internal::power_of_five_128[] #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) @@ -5731,22 +6825,27 @@ SIMDJSON_DLLIMPORTEXPORT const uint64_t simdjson::internal::power_of_five_128[] #endif #ifndef SIMDJSON_IMPLEMENTATION_LASX -#define SIMDJSON_IMPLEMENTATION_LASX (SIMDJSON_IS_LOONGARCH64 && __loongarch_asx) +#define SIMDJSON_IMPLEMENTATION_LASX (SIMDJSON_IS_LSX) #endif -#define SIMDJSON_CAN_ALWAYS_RUN_LASX (SIMDJSON_IMPLEMENTATION_LASX) +#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_LOONGARCH64 && __loongarch_sx) +#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 +#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 @@ -5772,6 +6871,8 @@ SIMDJSON_DLLIMPORTEXPORT const uint64_t simdjson::internal::power_of_five_128[] #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 @@ -5791,129 +6892,129 @@ SIMDJSON_DLLIMPORTEXPORT const uint64_t simdjson::internal::power_of_five_128[] #include namespace simdjson { // table modified and copied from - namespace internal { // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetTable - SIMDJSON_DLLIMPORTEXPORT const unsigned char BitsSetTable256mul2[256] = { - 0, 2, 2, 4, 2, 4, 4, 6, 2, 4, 4, 6, 4, 6, 6, 8, 2, 4, 4, - 6, 4, 6, 6, 8, 4, 6, 6, 8, 6, 8, 8, 10, 2, 4, 4, 6, 4, 6, - 6, 8, 4, 6, 6, 8, 6, 8, 8, 10, 4, 6, 6, 8, 6, 8, 8, 10, 6, - 8, 8, 10, 8, 10, 10, 12, 2, 4, 4, 6, 4, 6, 6, 8, 4, 6, 6, 8, - 6, 8, 8, 10, 4, 6, 6, 8, 6, 8, 8, 10, 6, 8, 8, 10, 8, 10, 10, - 12, 4, 6, 6, 8, 6, 8, 8, 10, 6, 8, 8, 10, 8, 10, 10, 12, 6, 8, - 8, 10, 8, 10, 10, 12, 8, 10, 10, 12, 10, 12, 12, 14, 2, 4, 4, 6, 4, - 6, 6, 8, 4, 6, 6, 8, 6, 8, 8, 10, 4, 6, 6, 8, 6, 8, 8, 10, - 6, 8, 8, 10, 8, 10, 10, 12, 4, 6, 6, 8, 6, 8, 8, 10, 6, 8, 8, - 10, 8, 10, 10, 12, 6, 8, 8, 10, 8, 10, 10, 12, 8, 10, 10, 12, 10, 12, - 12, 14, 4, 6, 6, 8, 6, 8, 8, 10, 6, 8, 8, 10, 8, 10, 10, 12, 6, - 8, 8, 10, 8, 10, 10, 12, 8, 10, 10, 12, 10, 12, 12, 14, 6, 8, 8, 10, - 8, 10, 10, 12, 8, 10, 10, 12, 10, 12, 12, 14, 8, 10, 10, 12, 10, 12, 12, - 14, 10, 12, 12, 14, 12, 14, 14, 16 }; +namespace internal { // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetTable +SIMDJSON_DLLIMPORTEXPORT const unsigned char BitsSetTable256mul2[256] = { + 0, 2, 2, 4, 2, 4, 4, 6, 2, 4, 4, 6, 4, 6, 6, 8, 2, 4, 4, + 6, 4, 6, 6, 8, 4, 6, 6, 8, 6, 8, 8, 10, 2, 4, 4, 6, 4, 6, + 6, 8, 4, 6, 6, 8, 6, 8, 8, 10, 4, 6, 6, 8, 6, 8, 8, 10, 6, + 8, 8, 10, 8, 10, 10, 12, 2, 4, 4, 6, 4, 6, 6, 8, 4, 6, 6, 8, + 6, 8, 8, 10, 4, 6, 6, 8, 6, 8, 8, 10, 6, 8, 8, 10, 8, 10, 10, + 12, 4, 6, 6, 8, 6, 8, 8, 10, 6, 8, 8, 10, 8, 10, 10, 12, 6, 8, + 8, 10, 8, 10, 10, 12, 8, 10, 10, 12, 10, 12, 12, 14, 2, 4, 4, 6, 4, + 6, 6, 8, 4, 6, 6, 8, 6, 8, 8, 10, 4, 6, 6, 8, 6, 8, 8, 10, + 6, 8, 8, 10, 8, 10, 10, 12, 4, 6, 6, 8, 6, 8, 8, 10, 6, 8, 8, + 10, 8, 10, 10, 12, 6, 8, 8, 10, 8, 10, 10, 12, 8, 10, 10, 12, 10, 12, + 12, 14, 4, 6, 6, 8, 6, 8, 8, 10, 6, 8, 8, 10, 8, 10, 10, 12, 6, + 8, 8, 10, 8, 10, 10, 12, 8, 10, 10, 12, 10, 12, 12, 14, 6, 8, 8, 10, + 8, 10, 10, 12, 8, 10, 10, 12, 10, 12, 12, 14, 8, 10, 10, 12, 10, 12, 12, + 14, 10, 12, 12, 14, 12, 14, 14, 16}; - SIMDJSON_DLLIMPORTEXPORT const uint8_t pshufb_combine_table[272] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, - 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08, - 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0x00, 0x01, 0x02, 0x03, - 0x04, 0x05, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, - 0x0f, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x08, 0x09, 0x0a, 0x0b, - 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x08, - 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x01, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, - 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x08, 0x09, 0x0a, 0x0b, - 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - }; +SIMDJSON_DLLIMPORTEXPORT const uint8_t pshufb_combine_table[272] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0x00, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x01, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; - // 256 * 8 bytes = 2kB, easily fits in cache. - SIMDJSON_DLLIMPORTEXPORT const uint64_t thintable_epi8[256] = { - 0x0706050403020100, 0x0007060504030201, 0x0007060504030200, - 0x0000070605040302, 0x0007060504030100, 0x0000070605040301, - 0x0000070605040300, 0x0000000706050403, 0x0007060504020100, - 0x0000070605040201, 0x0000070605040200, 0x0000000706050402, - 0x0000070605040100, 0x0000000706050401, 0x0000000706050400, - 0x0000000007060504, 0x0007060503020100, 0x0000070605030201, - 0x0000070605030200, 0x0000000706050302, 0x0000070605030100, - 0x0000000706050301, 0x0000000706050300, 0x0000000007060503, - 0x0000070605020100, 0x0000000706050201, 0x0000000706050200, - 0x0000000007060502, 0x0000000706050100, 0x0000000007060501, - 0x0000000007060500, 0x0000000000070605, 0x0007060403020100, - 0x0000070604030201, 0x0000070604030200, 0x0000000706040302, - 0x0000070604030100, 0x0000000706040301, 0x0000000706040300, - 0x0000000007060403, 0x0000070604020100, 0x0000000706040201, - 0x0000000706040200, 0x0000000007060402, 0x0000000706040100, - 0x0000000007060401, 0x0000000007060400, 0x0000000000070604, - 0x0000070603020100, 0x0000000706030201, 0x0000000706030200, - 0x0000000007060302, 0x0000000706030100, 0x0000000007060301, - 0x0000000007060300, 0x0000000000070603, 0x0000000706020100, - 0x0000000007060201, 0x0000000007060200, 0x0000000000070602, - 0x0000000007060100, 0x0000000000070601, 0x0000000000070600, - 0x0000000000000706, 0x0007050403020100, 0x0000070504030201, - 0x0000070504030200, 0x0000000705040302, 0x0000070504030100, - 0x0000000705040301, 0x0000000705040300, 0x0000000007050403, - 0x0000070504020100, 0x0000000705040201, 0x0000000705040200, - 0x0000000007050402, 0x0000000705040100, 0x0000000007050401, - 0x0000000007050400, 0x0000000000070504, 0x0000070503020100, - 0x0000000705030201, 0x0000000705030200, 0x0000000007050302, - 0x0000000705030100, 0x0000000007050301, 0x0000000007050300, - 0x0000000000070503, 0x0000000705020100, 0x0000000007050201, - 0x0000000007050200, 0x0000000000070502, 0x0000000007050100, - 0x0000000000070501, 0x0000000000070500, 0x0000000000000705, - 0x0000070403020100, 0x0000000704030201, 0x0000000704030200, - 0x0000000007040302, 0x0000000704030100, 0x0000000007040301, - 0x0000000007040300, 0x0000000000070403, 0x0000000704020100, - 0x0000000007040201, 0x0000000007040200, 0x0000000000070402, - 0x0000000007040100, 0x0000000000070401, 0x0000000000070400, - 0x0000000000000704, 0x0000000703020100, 0x0000000007030201, - 0x0000000007030200, 0x0000000000070302, 0x0000000007030100, - 0x0000000000070301, 0x0000000000070300, 0x0000000000000703, - 0x0000000007020100, 0x0000000000070201, 0x0000000000070200, - 0x0000000000000702, 0x0000000000070100, 0x0000000000000701, - 0x0000000000000700, 0x0000000000000007, 0x0006050403020100, - 0x0000060504030201, 0x0000060504030200, 0x0000000605040302, - 0x0000060504030100, 0x0000000605040301, 0x0000000605040300, - 0x0000000006050403, 0x0000060504020100, 0x0000000605040201, - 0x0000000605040200, 0x0000000006050402, 0x0000000605040100, - 0x0000000006050401, 0x0000000006050400, 0x0000000000060504, - 0x0000060503020100, 0x0000000605030201, 0x0000000605030200, - 0x0000000006050302, 0x0000000605030100, 0x0000000006050301, - 0x0000000006050300, 0x0000000000060503, 0x0000000605020100, - 0x0000000006050201, 0x0000000006050200, 0x0000000000060502, - 0x0000000006050100, 0x0000000000060501, 0x0000000000060500, - 0x0000000000000605, 0x0000060403020100, 0x0000000604030201, - 0x0000000604030200, 0x0000000006040302, 0x0000000604030100, - 0x0000000006040301, 0x0000000006040300, 0x0000000000060403, - 0x0000000604020100, 0x0000000006040201, 0x0000000006040200, - 0x0000000000060402, 0x0000000006040100, 0x0000000000060401, - 0x0000000000060400, 0x0000000000000604, 0x0000000603020100, - 0x0000000006030201, 0x0000000006030200, 0x0000000000060302, - 0x0000000006030100, 0x0000000000060301, 0x0000000000060300, - 0x0000000000000603, 0x0000000006020100, 0x0000000000060201, - 0x0000000000060200, 0x0000000000000602, 0x0000000000060100, - 0x0000000000000601, 0x0000000000000600, 0x0000000000000006, - 0x0000050403020100, 0x0000000504030201, 0x0000000504030200, - 0x0000000005040302, 0x0000000504030100, 0x0000000005040301, - 0x0000000005040300, 0x0000000000050403, 0x0000000504020100, - 0x0000000005040201, 0x0000000005040200, 0x0000000000050402, - 0x0000000005040100, 0x0000000000050401, 0x0000000000050400, - 0x0000000000000504, 0x0000000503020100, 0x0000000005030201, - 0x0000000005030200, 0x0000000000050302, 0x0000000005030100, - 0x0000000000050301, 0x0000000000050300, 0x0000000000000503, - 0x0000000005020100, 0x0000000000050201, 0x0000000000050200, - 0x0000000000000502, 0x0000000000050100, 0x0000000000000501, - 0x0000000000000500, 0x0000000000000005, 0x0000000403020100, - 0x0000000004030201, 0x0000000004030200, 0x0000000000040302, - 0x0000000004030100, 0x0000000000040301, 0x0000000000040300, - 0x0000000000000403, 0x0000000004020100, 0x0000000000040201, - 0x0000000000040200, 0x0000000000000402, 0x0000000000040100, - 0x0000000000000401, 0x0000000000000400, 0x0000000000000004, - 0x0000000003020100, 0x0000000000030201, 0x0000000000030200, - 0x0000000000000302, 0x0000000000030100, 0x0000000000000301, - 0x0000000000000300, 0x0000000000000003, 0x0000000000020100, - 0x0000000000000201, 0x0000000000000200, 0x0000000000000002, - 0x0000000000000100, 0x0000000000000001, 0x0000000000000000, - 0x0000000000000000, - }; //static uint64_t thintable_epi8[256] +// 256 * 8 bytes = 2kB, easily fits in cache. +SIMDJSON_DLLIMPORTEXPORT const uint64_t thintable_epi8[256] = { + 0x0706050403020100, 0x0007060504030201, 0x0007060504030200, + 0x0000070605040302, 0x0007060504030100, 0x0000070605040301, + 0x0000070605040300, 0x0000000706050403, 0x0007060504020100, + 0x0000070605040201, 0x0000070605040200, 0x0000000706050402, + 0x0000070605040100, 0x0000000706050401, 0x0000000706050400, + 0x0000000007060504, 0x0007060503020100, 0x0000070605030201, + 0x0000070605030200, 0x0000000706050302, 0x0000070605030100, + 0x0000000706050301, 0x0000000706050300, 0x0000000007060503, + 0x0000070605020100, 0x0000000706050201, 0x0000000706050200, + 0x0000000007060502, 0x0000000706050100, 0x0000000007060501, + 0x0000000007060500, 0x0000000000070605, 0x0007060403020100, + 0x0000070604030201, 0x0000070604030200, 0x0000000706040302, + 0x0000070604030100, 0x0000000706040301, 0x0000000706040300, + 0x0000000007060403, 0x0000070604020100, 0x0000000706040201, + 0x0000000706040200, 0x0000000007060402, 0x0000000706040100, + 0x0000000007060401, 0x0000000007060400, 0x0000000000070604, + 0x0000070603020100, 0x0000000706030201, 0x0000000706030200, + 0x0000000007060302, 0x0000000706030100, 0x0000000007060301, + 0x0000000007060300, 0x0000000000070603, 0x0000000706020100, + 0x0000000007060201, 0x0000000007060200, 0x0000000000070602, + 0x0000000007060100, 0x0000000000070601, 0x0000000000070600, + 0x0000000000000706, 0x0007050403020100, 0x0000070504030201, + 0x0000070504030200, 0x0000000705040302, 0x0000070504030100, + 0x0000000705040301, 0x0000000705040300, 0x0000000007050403, + 0x0000070504020100, 0x0000000705040201, 0x0000000705040200, + 0x0000000007050402, 0x0000000705040100, 0x0000000007050401, + 0x0000000007050400, 0x0000000000070504, 0x0000070503020100, + 0x0000000705030201, 0x0000000705030200, 0x0000000007050302, + 0x0000000705030100, 0x0000000007050301, 0x0000000007050300, + 0x0000000000070503, 0x0000000705020100, 0x0000000007050201, + 0x0000000007050200, 0x0000000000070502, 0x0000000007050100, + 0x0000000000070501, 0x0000000000070500, 0x0000000000000705, + 0x0000070403020100, 0x0000000704030201, 0x0000000704030200, + 0x0000000007040302, 0x0000000704030100, 0x0000000007040301, + 0x0000000007040300, 0x0000000000070403, 0x0000000704020100, + 0x0000000007040201, 0x0000000007040200, 0x0000000000070402, + 0x0000000007040100, 0x0000000000070401, 0x0000000000070400, + 0x0000000000000704, 0x0000000703020100, 0x0000000007030201, + 0x0000000007030200, 0x0000000000070302, 0x0000000007030100, + 0x0000000000070301, 0x0000000000070300, 0x0000000000000703, + 0x0000000007020100, 0x0000000000070201, 0x0000000000070200, + 0x0000000000000702, 0x0000000000070100, 0x0000000000000701, + 0x0000000000000700, 0x0000000000000007, 0x0006050403020100, + 0x0000060504030201, 0x0000060504030200, 0x0000000605040302, + 0x0000060504030100, 0x0000000605040301, 0x0000000605040300, + 0x0000000006050403, 0x0000060504020100, 0x0000000605040201, + 0x0000000605040200, 0x0000000006050402, 0x0000000605040100, + 0x0000000006050401, 0x0000000006050400, 0x0000000000060504, + 0x0000060503020100, 0x0000000605030201, 0x0000000605030200, + 0x0000000006050302, 0x0000000605030100, 0x0000000006050301, + 0x0000000006050300, 0x0000000000060503, 0x0000000605020100, + 0x0000000006050201, 0x0000000006050200, 0x0000000000060502, + 0x0000000006050100, 0x0000000000060501, 0x0000000000060500, + 0x0000000000000605, 0x0000060403020100, 0x0000000604030201, + 0x0000000604030200, 0x0000000006040302, 0x0000000604030100, + 0x0000000006040301, 0x0000000006040300, 0x0000000000060403, + 0x0000000604020100, 0x0000000006040201, 0x0000000006040200, + 0x0000000000060402, 0x0000000006040100, 0x0000000000060401, + 0x0000000000060400, 0x0000000000000604, 0x0000000603020100, + 0x0000000006030201, 0x0000000006030200, 0x0000000000060302, + 0x0000000006030100, 0x0000000000060301, 0x0000000000060300, + 0x0000000000000603, 0x0000000006020100, 0x0000000000060201, + 0x0000000000060200, 0x0000000000000602, 0x0000000000060100, + 0x0000000000000601, 0x0000000000000600, 0x0000000000000006, + 0x0000050403020100, 0x0000000504030201, 0x0000000504030200, + 0x0000000005040302, 0x0000000504030100, 0x0000000005040301, + 0x0000000005040300, 0x0000000000050403, 0x0000000504020100, + 0x0000000005040201, 0x0000000005040200, 0x0000000000050402, + 0x0000000005040100, 0x0000000000050401, 0x0000000000050400, + 0x0000000000000504, 0x0000000503020100, 0x0000000005030201, + 0x0000000005030200, 0x0000000000050302, 0x0000000005030100, + 0x0000000000050301, 0x0000000000050300, 0x0000000000000503, + 0x0000000005020100, 0x0000000000050201, 0x0000000000050200, + 0x0000000000000502, 0x0000000000050100, 0x0000000000000501, + 0x0000000000000500, 0x0000000000000005, 0x0000000403020100, + 0x0000000004030201, 0x0000000004030200, 0x0000000000040302, + 0x0000000004030100, 0x0000000000040301, 0x0000000000040300, + 0x0000000000000403, 0x0000000004020100, 0x0000000000040201, + 0x0000000000040200, 0x0000000000000402, 0x0000000000040100, + 0x0000000000000401, 0x0000000000000400, 0x0000000000000004, + 0x0000000003020100, 0x0000000000030201, 0x0000000000030200, + 0x0000000000000302, 0x0000000000030100, 0x0000000000000301, + 0x0000000000000300, 0x0000000000000003, 0x0000000000020100, + 0x0000000000000201, 0x0000000000000200, 0x0000000000000002, + 0x0000000000000100, 0x0000000000000001, 0x0000000000000000, + 0x0000000000000000, +}; //static uint64_t thintable_epi8[256] - } // namespace internal +} // namespace internal } // namespace simdjson #endif // SIMDJSON_IMPLEMENTATION_ARM64 || SIMDJSON_IMPLEMENTATION_ICELAKE || SIMDJSON_IMPLEMENTATION_HASWELL || SIMDJSON_IMPLEMENTATION_WESTMERE || SIMDJSON_IMPLEMENTATION_PPC64 || SIMDJSON_IMPLEMENTATION_LSX || SIMDJSON_IMPLEMENTATION_LASX @@ -5948,27 +7049,27 @@ namespace simdjson { // table modified and copied from #include namespace simdjson { - namespace internal { +namespace internal { - template - class atomic_ptr { - public: - atomic_ptr(T* _ptr) : ptr{ _ptr } {} +template +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 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; } + 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 ptr; - }; +private: + std::atomic ptr; +}; - } // namespace internal +} // namespace internal } // namespace simdjson #endif // SIMDJSON_INTERNAL_ATOMIC_PTR_H @@ -5984,246 +7085,246 @@ namespace simdjson { namespace simdjson { - namespace dom { - class document; - } // namespace dom +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 }; +/** +* 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}; - /** - * 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); - } +/** + * 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 { +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: +/** + * 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 + * + * 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 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. + * + * 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; + /** + * @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 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; + /** + * 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 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; + /** + * 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; + /** + * 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 structural_indexes{}; - /** Next structural index to parse */ - uint32_t next_structural_index{ 0 }; + /** 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 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 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; + /** + * 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; + /** + * 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 }; +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 }; + /** + * The maximum depth (number of nested objects and arrays) supported by this parser. + * + * Defaults to DEFAULT_MAX_DEPTH. + */ + size_t _max_depth{0}; - // 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; + // 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(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_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::capacity() const noexcept { + return _capacity; +} - simdjson_pure simdjson_inline size_t dom_parser_implementation::max_depth() const noexcept { - return _max_depth; - } +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; - } +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 internal } // namespace simdjson #endif // SIMDJSON_INTERNAL_DOM_PARSER_IMPLEMENTATION_H @@ -6233,222 +7334,222 @@ namespace simdjson { 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 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 &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; + } - /** - * 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()); - } + /** + * 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; +}; - /** - * 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: +} // namespace internal - /** - * 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 list of available implementations compiled into simdjson. + */ +extern SIMDJSON_DLLIMPORTEXPORT const internal::available_implementation_list& get_available_implementations(); - /** - * 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& 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& get_active_implementation(); +/** + * The active implementation. + * + * Automatically initialized on first use to the most advanced implementation supported by this hardware. + */ +extern SIMDJSON_DLLIMPORTEXPORT internal::atomic_ptr& get_active_implementation(); } // namespace simdjson @@ -6506,31 +7607,33 @@ POSSIBILITY OF SUCH DAMAGE. #define SIMDJSON_INTERNAL_INSTRUCTION_SET_H namespace simdjson { - namespace internal { +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, - }; +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, +}; - } // namespace internal +} // namespace internal } // namespace simdjson #endif // SIMDJSON_INTERNAL_INSTRUCTION_SET_H @@ -6548,21 +7651,20 @@ namespace simdjson { #include namespace simdjson { // table modified and copied from - namespace internal { // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetTable +namespace internal { // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetTable - extern SIMDJSON_DLLIMPORTEXPORT const unsigned char BitsSetTable256mul2[256]; +extern SIMDJSON_DLLIMPORTEXPORT const unsigned char BitsSetTable256mul2[256]; - extern SIMDJSON_DLLIMPORTEXPORT const uint8_t pshufb_combine_table[272]; +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]; +// 256 * 8 bytes = 2kB, easily fits in cache. +extern SIMDJSON_DLLIMPORTEXPORT const uint64_t thintable_epi8[256]; - } // namespace internal +} // 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 */ /* including generic/dependencies.h: #include */ @@ -6604,49 +7706,49 @@ namespace simdjson { // table modified and copied from namespace simdjson { - /** - * @brief A DOM API on top of the simdjson parser. - */ - namespace dom { +/** + * @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; +/** 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; +/** + * 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; +class array; +class document; +class document_stream; +class element; +class key_value_pair; +class object; +class parser; #ifdef SIMDJSON_THREADS_ENABLED - struct stage1_worker; +struct stage1_worker; #endif // SIMDJSON_THREADS_ENABLED - } // namespace dom +} // namespace dom - namespace internal { +namespace internal { - template - class string_builder; - class tape_ref; +template +class string_builder; +class tape_ref; - } // namespace internal +} // namespace internal } // namespace simdjson @@ -6656,86 +7758,86 @@ namespace simdjson { #include namespace simdjson { - namespace dom { +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; +/** + * 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 + /** + * 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; + /** + * 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 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 tape{}; + /** @private Structural values. */ + std::unique_ptr tape{}; - /** @private String values. - * - * Should be at least byte_capacity. - */ - std::unique_ptr 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. - * Passsing 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 String values. + * + * Should be at least byte_capacity. + */ + std::unique_ptr 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 +private: + size_t allocated_capacity{0}; + friend class parser; +}; // class document - } // namespace dom +} // namespace dom } // namespace simdjson #endif // SIMDJSON_DOM_DOCUMENT_H @@ -6746,27 +7848,27 @@ namespace simdjson { #define SIMDJSON_INTERNAL_TAPE_TYPE_H namespace simdjson { - namespace internal { +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' - }; // enum class tape_type +/** + * 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' +}; // enum class tape_type - } // namespace internal +} // namespace internal } // namespace simdjson #endif // SIMDJSON_INTERNAL_TAPE_TYPE_H @@ -6842,194 +7944,211 @@ POSSIBILITY OF SUCH DAMAGE. #elif defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID) #include #endif +#if defined(__loongarch__) && defined(__linux__) + #include +#endif namespace simdjson { - namespace internal { +namespace internal { #if defined(__PPC64__) - static inline uint32_t detect_supported_architectures() { - return instruction_set::ALTIVEC; - } +static inline uint32_t detect_supported_architectures() { + return instruction_set::ALTIVEC; +} #elif defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) - static inline uint32_t detect_supported_architectures() { - return instruction_set::NEON; - } +static inline uint32_t detect_supported_architectures() { + return instruction_set::NEON; +} #elif defined(__x86_64__) || defined(_M_AMD64) // x64 - namespace { - // Can be found on Intel ISA Reference for CPUID - constexpr uint32_t cpuid_avx2_bit = 1 << 5; ///< @private Bit 5 of EBX for EAX=0x7 - constexpr uint32_t cpuid_bmi1_bit = 1 << 3; ///< @private bit 3 of EBX for EAX=0x7 - constexpr uint32_t cpuid_bmi2_bit = 1 << 8; ///< @private bit 8 of EBX for EAX=0x7 - constexpr uint32_t cpuid_avx512f_bit = 1 << 16; ///< @private bit 16 of EBX for EAX=0x7 - constexpr uint32_t cpuid_avx512dq_bit = 1 << 17; ///< @private bit 17 of EBX for EAX=0x7 - constexpr uint32_t cpuid_avx512ifma_bit = 1 << 21; ///< @private bit 21 of EBX for EAX=0x7 - constexpr uint32_t cpuid_avx512pf_bit = 1 << 26; ///< @private bit 26 of EBX for EAX=0x7 - constexpr uint32_t cpuid_avx512er_bit = 1 << 27; ///< @private bit 27 of EBX for EAX=0x7 - constexpr uint32_t cpuid_avx512cd_bit = 1 << 28; ///< @private bit 28 of EBX for EAX=0x7 - constexpr uint32_t cpuid_avx512bw_bit = 1 << 30; ///< @private bit 30 of EBX for EAX=0x7 - constexpr uint32_t cpuid_avx512vl_bit = 1U << 31; ///< @private bit 31 of EBX for EAX=0x7 - constexpr uint32_t cpuid_avx512vbmi2_bit = 1 << 6; ///< @private bit 6 of ECX for EAX=0x7 - constexpr uint64_t cpuid_avx256_saved = uint64_t(1) << 2; ///< @private bit 2 = AVX - constexpr uint64_t cpuid_avx512_saved = uint64_t(7) << 5; ///< @private bits 5,6,7 = opmask, ZMM_hi256, hi16_ZMM - constexpr uint32_t cpuid_sse42_bit = 1 << 20; ///< @private bit 20 of ECX for EAX=0x1 - constexpr uint32_t cpuid_osxsave = (uint32_t(1) << 26) | (uint32_t(1) << 27); ///< @private bits 26+27 of ECX for EAX=0x1 - constexpr uint32_t cpuid_pclmulqdq_bit = 1 << 1; ///< @private bit 1 of ECX for EAX=0x1 - } +namespace { +// Can be found on Intel ISA Reference for CPUID +constexpr uint32_t cpuid_avx2_bit = 1 << 5; ///< @private Bit 5 of EBX for EAX=0x7 +constexpr uint32_t cpuid_bmi1_bit = 1 << 3; ///< @private bit 3 of EBX for EAX=0x7 +constexpr uint32_t cpuid_bmi2_bit = 1 << 8; ///< @private bit 8 of EBX for EAX=0x7 +constexpr uint32_t cpuid_avx512f_bit = 1 << 16; ///< @private bit 16 of EBX for EAX=0x7 +constexpr uint32_t cpuid_avx512dq_bit = 1 << 17; ///< @private bit 17 of EBX for EAX=0x7 +constexpr uint32_t cpuid_avx512ifma_bit = 1 << 21; ///< @private bit 21 of EBX for EAX=0x7 +constexpr uint32_t cpuid_avx512pf_bit = 1 << 26; ///< @private bit 26 of EBX for EAX=0x7 +constexpr uint32_t cpuid_avx512er_bit = 1 << 27; ///< @private bit 27 of EBX for EAX=0x7 +constexpr uint32_t cpuid_avx512cd_bit = 1 << 28; ///< @private bit 28 of EBX for EAX=0x7 +constexpr uint32_t cpuid_avx512bw_bit = 1 << 30; ///< @private bit 30 of EBX for EAX=0x7 +constexpr uint32_t cpuid_avx512vl_bit = 1U << 31; ///< @private bit 31 of EBX for EAX=0x7 +constexpr uint32_t cpuid_avx512vbmi2_bit = 1 << 6; ///< @private bit 6 of ECX for EAX=0x7 +constexpr uint64_t cpuid_avx256_saved = uint64_t(1) << 2; ///< @private bit 2 = AVX +constexpr uint64_t cpuid_avx512_saved = uint64_t(7) << 5; ///< @private bits 5,6,7 = opmask, ZMM_hi256, hi16_ZMM +constexpr uint32_t cpuid_sse42_bit = 1 << 20; ///< @private bit 20 of ECX for EAX=0x1 +constexpr uint32_t cpuid_osxsave = (uint32_t(1) << 26) | (uint32_t(1) << 27); ///< @private bits 26+27 of ECX for EAX=0x1 +constexpr uint32_t cpuid_pclmulqdq_bit = 1 << 1; ///< @private bit 1 of ECX for EAX=0x1 +} - static inline void cpuid(uint32_t* eax, uint32_t* ebx, uint32_t* ecx, - uint32_t* edx) { +static inline void cpuid(uint32_t *eax, uint32_t *ebx, uint32_t *ecx, + uint32_t *edx) { #if defined(_MSC_VER) - int cpu_info[4]; - __cpuidex(cpu_info, *eax, *ecx); - *eax = cpu_info[0]; - *ebx = cpu_info[1]; - *ecx = cpu_info[2]; - *edx = cpu_info[3]; + int cpu_info[4]; + __cpuidex(cpu_info, *eax, *ecx); + *eax = cpu_info[0]; + *ebx = cpu_info[1]; + *ecx = cpu_info[2]; + *edx = cpu_info[3]; #elif defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID) - uint32_t level = *eax; - __get_cpuid(level, eax, ebx, ecx, edx); + uint32_t level = *eax; + __get_cpuid(level, eax, ebx, ecx, edx); #else - uint32_t a = *eax, b, c = *ecx, d; - asm volatile("cpuid\n\t" : "+a"(a), "=b"(b), "+c"(c), "=d"(d)); - *eax = a; - *ebx = b; - *ecx = c; - *edx = d; + uint32_t a = *eax, b, c = *ecx, d; + asm volatile("cpuid\n\t" : "+a"(a), "=b"(b), "+c"(c), "=d"(d)); + *eax = a; + *ebx = b; + *ecx = c; + *edx = d; #endif - } +} - static inline uint64_t xgetbv() { +static inline uint64_t xgetbv() { #if defined(_MSC_VER) - return _xgetbv(0); + return _xgetbv(0); #else - uint32_t xcr0_lo, xcr0_hi; - asm volatile("xgetbv\n\t" : "=a" (xcr0_lo), "=d" (xcr0_hi) : "c" (0)); - return xcr0_lo | (uint64_t(xcr0_hi) << 32); + uint32_t xcr0_lo, xcr0_hi; + asm volatile("xgetbv\n\t" : "=a" (xcr0_lo), "=d" (xcr0_hi) : "c" (0)); + return xcr0_lo | (uint64_t(xcr0_hi) << 32); #endif - } +} - static inline uint32_t detect_supported_architectures() { - uint32_t eax, ebx, ecx, edx; - uint32_t host_isa = 0x0; +static inline uint32_t detect_supported_architectures() { + uint32_t eax, ebx, ecx, edx; + uint32_t host_isa = 0x0; - // EBX for EAX=0x1 - eax = 0x1; - ecx = 0x0; - cpuid(&eax, &ebx, &ecx, &edx); + // EBX for EAX=0x1 + eax = 0x1; + ecx = 0x0; + cpuid(&eax, &ebx, &ecx, &edx); - if (ecx & cpuid_sse42_bit) { - host_isa |= instruction_set::SSE42; - } - else { - return host_isa; // everything after is redundant - } + if (ecx & cpuid_sse42_bit) { + host_isa |= instruction_set::SSE42; + } else { + return host_isa; // everything after is redundant + } - if (ecx & cpuid_pclmulqdq_bit) { - host_isa |= instruction_set::PCLMULQDQ; - } + if (ecx & cpuid_pclmulqdq_bit) { + host_isa |= instruction_set::PCLMULQDQ; + } - if ((ecx & cpuid_osxsave) != cpuid_osxsave) { - return host_isa; - } + if ((ecx & cpuid_osxsave) != cpuid_osxsave) { + return host_isa; + } - // xgetbv for checking if the OS saves registers - uint64_t xcr0 = xgetbv(); + // xgetbv for checking if the OS saves registers + uint64_t xcr0 = xgetbv(); - if ((xcr0 & cpuid_avx256_saved) == 0) { - return host_isa; - } + if ((xcr0 & cpuid_avx256_saved) == 0) { + return host_isa; + } - // ECX for EAX=0x7 - eax = 0x7; - ecx = 0x0; - cpuid(&eax, &ebx, &ecx, &edx); - if (ebx & cpuid_avx2_bit) { - host_isa |= instruction_set::AVX2; - } - if (ebx & cpuid_bmi1_bit) { - host_isa |= instruction_set::BMI1; - } + // ECX for EAX=0x7 + eax = 0x7; + ecx = 0x0; + cpuid(&eax, &ebx, &ecx, &edx); + if (ebx & cpuid_avx2_bit) { + host_isa |= instruction_set::AVX2; + } + if (ebx & cpuid_bmi1_bit) { + host_isa |= instruction_set::BMI1; + } - if (ebx & cpuid_bmi2_bit) { - host_isa |= instruction_set::BMI2; - } + if (ebx & cpuid_bmi2_bit) { + host_isa |= instruction_set::BMI2; + } - if (!((xcr0 & cpuid_avx512_saved) == cpuid_avx512_saved)) { - return host_isa; - } + if (!((xcr0 & cpuid_avx512_saved) == cpuid_avx512_saved)) { + return host_isa; + } - if (ebx & cpuid_avx512f_bit) { - host_isa |= instruction_set::AVX512F; - } + if (ebx & cpuid_avx512f_bit) { + host_isa |= instruction_set::AVX512F; + } - if (ebx & cpuid_avx512dq_bit) { - host_isa |= instruction_set::AVX512DQ; - } + if (ebx & cpuid_avx512dq_bit) { + host_isa |= instruction_set::AVX512DQ; + } - if (ebx & cpuid_avx512ifma_bit) { - host_isa |= instruction_set::AVX512IFMA; - } + if (ebx & cpuid_avx512ifma_bit) { + host_isa |= instruction_set::AVX512IFMA; + } - if (ebx & cpuid_avx512pf_bit) { - host_isa |= instruction_set::AVX512PF; - } + if (ebx & cpuid_avx512pf_bit) { + host_isa |= instruction_set::AVX512PF; + } - if (ebx & cpuid_avx512er_bit) { - host_isa |= instruction_set::AVX512ER; - } + if (ebx & cpuid_avx512er_bit) { + host_isa |= instruction_set::AVX512ER; + } - if (ebx & cpuid_avx512cd_bit) { - host_isa |= instruction_set::AVX512CD; - } + if (ebx & cpuid_avx512cd_bit) { + host_isa |= instruction_set::AVX512CD; + } - if (ebx & cpuid_avx512bw_bit) { - host_isa |= instruction_set::AVX512BW; - } + if (ebx & cpuid_avx512bw_bit) { + host_isa |= instruction_set::AVX512BW; + } - if (ebx & cpuid_avx512vl_bit) { - host_isa |= instruction_set::AVX512VL; - } + if (ebx & cpuid_avx512vl_bit) { + host_isa |= instruction_set::AVX512VL; + } - if (ecx & cpuid_avx512vbmi2_bit) { - host_isa |= instruction_set::AVX512VBMI2; - } + if (ecx & cpuid_avx512vbmi2_bit) { + host_isa |= instruction_set::AVX512VBMI2; + } - return host_isa; - } + return host_isa; +} -#elif defined(__loongarch_sx) && !defined(__loongarch_asx) +#elif defined(__loongarch__) - static inline uint32_t detect_supported_architectures() { - return instruction_set::LSX; - } +static inline uint32_t detect_supported_architectures() { + uint32_t host_isa = instruction_set::DEFAULT; + #if defined(__linux__) + uint64_t hwcap = 0; + hwcap = getauxval(AT_HWCAP); + if (hwcap & HWCAP_LOONGARCH_LSX) { + host_isa |= instruction_set::LSX; + } + if (hwcap & HWCAP_LOONGARCH_LASX) { + host_isa |= instruction_set::LASX; + } + #endif + return host_isa; +} -#elif defined(__loongarch_asx) +#elif SIMDJSON_IS_RISCV64 - static inline uint32_t detect_supported_architectures() { - return instruction_set::LASX; - } +static inline uint32_t detect_supported_architectures() { + uint32_t host_isa = instruction_set::DEFAULT; +#if SIMDJSON_IS_RVV_VLS + host_isa |= instruction_set::RVV_VLS; +#endif + return host_isa; +} #else // fallback - static inline uint32_t detect_supported_architectures() { - return instruction_set::DEFAULT; - } +static inline uint32_t detect_supported_architectures() { + return instruction_set::DEFAULT; +} #endif // end SIMD extension detection code - } // namespace internal +} // namespace internal } // namespace simdjson #endif // SIMDJSON_INTERNAL_ISADETECTION_H @@ -7040,11 +8159,11 @@ namespace simdjson { namespace simdjson { - bool implementation::supported_by_runtime_system() const { - uint32_t required_instruction_sets = this->required_instruction_sets(); - uint32_t supported_instruction_sets = internal::detect_supported_architectures(); - return ((supported_instruction_sets & required_instruction_sets) == required_instruction_sets); - } +bool implementation::supported_by_runtime_system() const { + uint32_t required_instruction_sets = this->required_instruction_sets(); + uint32_t supported_instruction_sets = internal::detect_supported_architectures(); + return ((supported_instruction_sets & required_instruction_sets) == required_instruction_sets); +} } // namespace simdjson @@ -7064,35 +8183,35 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { +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& 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; - }; +/** + * @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& 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 arm64 } // namespace simdjson #endif // SIMDJSON_ARM64_IMPLEMENTATION_H /* end file simdjson/arm64/implementation.h */ namespace simdjson { - namespace internal { - static const arm64::implementation* get_arm64_singleton() { - static const arm64::implementation arm64_singleton{}; - return &arm64_singleton; - } - } // namespace internal +namespace internal { +static const arm64::implementation* get_arm64_singleton() { + static const arm64::implementation arm64_singleton{}; + return &arm64_singleton; +} +} // namespace internal } // namespace simdjson #endif // SIMDJSON_IMPLEMENTATION_ARM64 @@ -7108,39 +8227,39 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace fallback { +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& 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; - }; +/** + * @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& 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 fallback } // namespace simdjson #endif // SIMDJSON_FALLBACK_IMPLEMENTATION_H /* end file simdjson/fallback/implementation.h */ namespace simdjson { - namespace internal { - static const fallback::implementation* get_fallback_singleton() { - static const fallback::implementation fallback_singleton{}; - return &fallback_singleton; - } - } // namespace internal +namespace internal { +static const fallback::implementation* get_fallback_singleton() { + static const fallback::implementation fallback_singleton{}; + return &fallback_singleton; +} +} // namespace internal } // namespace simdjson #endif // SIMDJSON_IMPLEMENTATION_FALLBACK @@ -7159,39 +8278,39 @@ namespace simdjson { // The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_HASWELL namespace simdjson { - namespace haswell { +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& 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; - }; +/** + * @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& 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 haswell } // namespace simdjson #endif // SIMDJSON_HASWELL_IMPLEMENTATION_H /* end file simdjson/haswell/implementation.h */ namespace simdjson { - namespace internal { - static const haswell::implementation* get_haswell_singleton() { - static const haswell::implementation haswell_singleton{}; - return &haswell_singleton; - } - } // namespace internal +namespace internal { +static const haswell::implementation* get_haswell_singleton() { + static const haswell::implementation haswell_singleton{}; + return &haswell_singleton; +} +} // namespace internal } // namespace simdjson #endif @@ -7209,39 +8328,39 @@ namespace simdjson { // The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_ICELAKE namespace simdjson { - namespace icelake { +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& 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; - }; +/** + * @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& 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 icelake } // namespace simdjson #endif // SIMDJSON_ICELAKE_IMPLEMENTATION_H /* end file simdjson/icelake/implementation.h */ namespace simdjson { - namespace internal { - static const icelake::implementation* get_icelake_singleton() { - static const icelake::implementation icelake_singleton{}; - return &icelake_singleton; - } - } // namespace internal +namespace internal { +static const icelake::implementation* get_icelake_singleton() { + static const icelake::implementation icelake_singleton{}; + return &icelake_singleton; +} +} // namespace internal } // namespace simdjson #endif @@ -7259,43 +8378,43 @@ namespace simdjson { namespace simdjson { - /** - * Implementation for ALTIVEC (PPC64). - */ - namespace ppc64 { +/** + * 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) {} +/** + * @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& 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; - }; + simdjson_warn_unused error_code create_dom_parser_implementation( + size_t capacity, size_t max_length, + std::unique_ptr &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 ppc64 } // namespace simdjson #endif // SIMDJSON_PPC64_IMPLEMENTATION_H /* end file simdjson/ppc64/implementation.h */ namespace simdjson { - namespace internal { - static const ppc64::implementation* get_ppc64_singleton() { - static const ppc64::implementation ppc64_singleton{}; - return &ppc64_singleton; - } - } // namespace internal +namespace internal { +static const ppc64::implementation* get_ppc64_singleton() { + static const ppc64::implementation ppc64_singleton{}; + return &ppc64_singleton; +} +} // namespace internal } // namespace simdjson #endif // SIMDJSON_IMPLEMENTATION_PPC64 @@ -7313,83 +8432,38 @@ namespace simdjson { // The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_WESTMERE namespace simdjson { - namespace westmere { +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& 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; - }; +/** + * @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& 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 westmere } // namespace simdjson #endif // SIMDJSON_WESTMERE_IMPLEMENTATION_H /* end file simdjson/westmere/implementation.h */ namespace simdjson { - namespace internal { - static const simdjson::westmere::implementation* get_westmere_singleton() { - static const simdjson::westmere::implementation westmere_singleton{}; - return &westmere_singleton; - } - } // namespace internal +namespace internal { +static const simdjson::westmere::implementation* get_westmere_singleton() { + static const simdjson::westmere::implementation westmere_singleton{}; + return &westmere_singleton; +} +} // namespace internal } // namespace simdjson #endif // SIMDJSON_IMPLEMENTATION_WESTMERE -#if SIMDJSON_IMPLEMENTATION_LSX -/* including simdjson/lsx/implementation.h: #include */ -/* 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& 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 */ -namespace simdjson { - namespace internal { - static const simdjson::lsx::implementation* get_lsx_singleton() { - static const simdjson::lsx::implementation lsx_singleton{}; - return &lsx_singleton; - } - } // namespace internal -} // namespace simdjson -#endif // SIMDJSON_IMPLEMENTATION_LSX - #if SIMDJSON_IMPLEMENTATION_LASX /* including simdjson/lasx/implementation.h: #include */ /* begin file simdjson/lasx/implementation.h */ @@ -7403,246 +8477,344 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { +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& 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; - }; +/** + * @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& 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 lasx } // namespace simdjson #endif // SIMDJSON_LASX_IMPLEMENTATION_H /* end file simdjson/lasx/implementation.h */ namespace simdjson { - namespace internal { - static const simdjson::lasx::implementation* get_lasx_singleton() { - static const simdjson::lasx::implementation lasx_singleton{}; - return &lasx_singleton; - } - } // namespace internal +namespace internal { +static const simdjson::lasx::implementation* get_lasx_singleton() { + static const simdjson::lasx::implementation lasx_singleton{}; + return &lasx_singleton; +} +} // namespace internal } // namespace simdjson #endif // SIMDJSON_IMPLEMENTATION_LASX +#if SIMDJSON_IMPLEMENTATION_LSX +/* including simdjson/lsx/implementation.h: #include */ +/* 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& 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 */ +namespace simdjson { +namespace internal { +static const simdjson::lsx::implementation* get_lsx_singleton() { + static const simdjson::lsx::implementation lsx_singleton{}; + return &lsx_singleton; +} +} // namespace internal +} // namespace simdjson +#endif // SIMDJSON_IMPLEMENTATION_LSX + +#if SIMDJSON_IMPLEMENTATION_RVV_VLS +/* including simdjson/rvv-vls/implementation.h: #include */ +/* 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", + 0 + ) {} + simdjson_warn_unused error_code create_dom_parser_implementation( + size_t capacity, + size_t max_length, + std::unique_ptr& 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 */ +namespace simdjson { +namespace internal { +static const simdjson::rvv_vls::implementation* get_rvv_vls_singleton() { + static const simdjson::rvv_vls::implementation rvv_vls_singleton{}; + return &rvv_vls_singleton; +} +} // namespace internal +} // namespace simdjson +#endif // SIMDJSON_IMPLEMENTATION_RVV_VLS + /* undefining SIMDJSON_CONDITIONAL_INCLUDE */ #undef SIMDJSON_CONDITIONAL_INCLUDE namespace simdjson { - namespace internal { +namespace internal { - // When there is a single implementation, we should not pay a price - // for dispatching to the best implementation. We should just use the - // one we have. This is a compile-time check. +// When there is a single implementation, we should not pay a price +// for dispatching to the best implementation. We should just use the +// one we have. This is a compile-time check. #define SIMDJSON_SINGLE_IMPLEMENTATION (SIMDJSON_IMPLEMENTATION_ICELAKE \ + SIMDJSON_IMPLEMENTATION_HASWELL + SIMDJSON_IMPLEMENTATION_WESTMERE \ + SIMDJSON_IMPLEMENTATION_ARM64 + SIMDJSON_IMPLEMENTATION_PPC64 \ + SIMDJSON_IMPLEMENTATION_LSX + SIMDJSON_IMPLEMENTATION_LASX \ - + SIMDJSON_IMPLEMENTATION_FALLBACK == 1) + + SIMDJSON_IMPLEMENTATION_RVV_VLS + SIMDJSON_IMPLEMENTATION_FALLBACK == 1) #if SIMDJSON_SINGLE_IMPLEMENTATION - static const implementation* get_single_implementation() { - return + simdjson_really_inline static const implementation* get_single_implementation() { + return #if SIMDJSON_IMPLEMENTATION_ICELAKE - get_icelake_singleton(); + get_icelake_singleton(); #endif #if SIMDJSON_IMPLEMENTATION_HASWELL - get_haswell_singleton(); + get_haswell_singleton(); #endif #if SIMDJSON_IMPLEMENTATION_WESTMERE - get_westmere_singleton(); + get_westmere_singleton(); #endif #if SIMDJSON_IMPLEMENTATION_ARM64 - get_arm64_singleton(); + get_arm64_singleton(); #endif #if SIMDJSON_IMPLEMENTATION_PPC64 - get_ppc64_singleton(); + get_ppc64_singleton(); #endif #if SIMDJSON_IMPLEMENTATION_LSX - get_lsx_singleton(); + get_lsx_singleton(); #endif #if SIMDJSON_IMPLEMENTATION_LASX - get_lasx_singleton(); + get_lasx_singleton(); +#endif +#if SIMDJSON_IMPLEMENTATION_RVV_VLS + get_rvv_vls_singleton(); #endif #if SIMDJSON_IMPLEMENTATION_FALLBACK - get_fallback_singleton(); + get_fallback_singleton(); #endif - } +} #endif - // Static array of known implementations. We're hoping these get baked into the executable - // without requiring a static initializer. +// Static array of known implementations. We're hoping these get baked into the executable +// without requiring a static initializer. - /** - * @private Detects best supported implementation on first use, and sets it - */ - class detect_best_supported_implementation_on_first_use final : public implementation { - public: - std::string name() const noexcept final { return set_best()->name(); } - std::string description() const noexcept final { return set_best()->description(); } - uint32_t required_instruction_sets() const noexcept final { return set_best()->required_instruction_sets(); } - simdjson_warn_unused error_code create_dom_parser_implementation( - size_t capacity, - size_t max_length, - std::unique_ptr& dst - ) const noexcept final { - return set_best()->create_dom_parser_implementation(capacity, max_length, dst); - } - simdjson_warn_unused error_code minify(const uint8_t* buf, size_t len, uint8_t* dst, size_t& dst_len) const noexcept final { - return set_best()->minify(buf, len, dst, dst_len); - } - simdjson_warn_unused bool validate_utf8(const char* buf, size_t len) const noexcept final override { - return set_best()->validate_utf8(buf, len); - } - simdjson_inline detect_best_supported_implementation_on_first_use() noexcept : implementation("best_supported_detector", "Detects the best supported implementation and sets it", 0) {} - private: - const implementation* set_best() const noexcept; - }; +/** + * @private Detects best supported implementation on first use, and sets it + */ +class detect_best_supported_implementation_on_first_use final : public implementation { +public: + std::string name() const noexcept final { return set_best()->name(); } + std::string description() const noexcept final { return set_best()->description(); } + uint32_t required_instruction_sets() const noexcept final { return set_best()->required_instruction_sets(); } + simdjson_warn_unused error_code create_dom_parser_implementation( + size_t capacity, + size_t max_length, + std::unique_ptr& dst + ) const noexcept final { + return set_best()->create_dom_parser_implementation(capacity, max_length, dst); + } + simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final { + return set_best()->minify(buf, len, dst, dst_len); + } + simdjson_warn_unused bool validate_utf8(const char * buf, size_t len) const noexcept final override { + return set_best()->validate_utf8(buf, len); + } + simdjson_inline detect_best_supported_implementation_on_first_use() noexcept : implementation("best_supported_detector", "Detects the best supported implementation and sets it", 0) {} +private: + const implementation *set_best() const noexcept; +}; - static_assert(std::is_trivially_destructible::value, "detect_best_supported_implementation_on_first_use should be trivially destructible"); +static_assert(std::is_trivially_destructible::value, "detect_best_supported_implementation_on_first_use should be trivially destructible"); - static const std::initializer_list& get_available_implementation_pointers() { - static const std::initializer_list available_implementation_pointers{ - #if SIMDJSON_IMPLEMENTATION_ICELAKE - get_icelake_singleton(), - #endif - #if SIMDJSON_IMPLEMENTATION_HASWELL - get_haswell_singleton(), - #endif - #if SIMDJSON_IMPLEMENTATION_WESTMERE - get_westmere_singleton(), - #endif - #if SIMDJSON_IMPLEMENTATION_ARM64 - get_arm64_singleton(), - #endif - #if SIMDJSON_IMPLEMENTATION_PPC64 - get_ppc64_singleton(), - #endif - #if SIMDJSON_IMPLEMENTATION_LSX - get_lsx_singleton(), - #endif - #if SIMDJSON_IMPLEMENTATION_LASX - get_lasx_singleton(), - #endif - #if SIMDJSON_IMPLEMENTATION_FALLBACK - get_fallback_singleton(), - #endif - }; // available_implementation_pointers - return available_implementation_pointers; - } +static const std::initializer_list& get_available_implementation_pointers() { + static const std::initializer_list available_implementation_pointers { +#if SIMDJSON_IMPLEMENTATION_ICELAKE + get_icelake_singleton(), +#endif +#if SIMDJSON_IMPLEMENTATION_HASWELL + get_haswell_singleton(), +#endif +#if SIMDJSON_IMPLEMENTATION_WESTMERE + get_westmere_singleton(), +#endif +#if SIMDJSON_IMPLEMENTATION_ARM64 + get_arm64_singleton(), +#endif +#if SIMDJSON_IMPLEMENTATION_PPC64 + get_ppc64_singleton(), +#endif +#if SIMDJSON_IMPLEMENTATION_LASX + get_lasx_singleton(), +#endif +#if SIMDJSON_IMPLEMENTATION_LSX + get_lsx_singleton(), +#endif +#if SIMDJSON_IMPLEMENTATION_RVV_VLS + get_rvv_vls_singleton(), +#endif +#if SIMDJSON_IMPLEMENTATION_FALLBACK + get_fallback_singleton(), +#endif + }; // available_implementation_pointers + return available_implementation_pointers; +} - // So we can return UNSUPPORTED_ARCHITECTURE from the parser when there is no support - class unsupported_implementation final : public implementation { - public: - simdjson_warn_unused error_code create_dom_parser_implementation( - size_t, - size_t, - std::unique_ptr& - ) const noexcept final { - return UNSUPPORTED_ARCHITECTURE; - } - simdjson_warn_unused error_code minify(const uint8_t*, size_t, uint8_t*, size_t&) const noexcept final override { - return UNSUPPORTED_ARCHITECTURE; - } - simdjson_warn_unused bool validate_utf8(const char*, size_t) const noexcept final override { - return false; // Just refuse to validate. Given that we have a fallback implementation - // it seems unlikely that unsupported_implementation will ever be used. If it is used, - // then it will flag all strings as invalid. The alternative is to return an error_code - // from which the user has to figure out whether the string is valid UTF-8... which seems - // like a lot of work just to handle the very unlikely case that we have an unsupported - // implementation. And, when it does happen (that we have an unsupported implementation), - // what are the chances that the programmer has a fallback? Given that *we* provide the - // fallback, it implies that the programmer would need a fallback for our fallback. - } - unsupported_implementation() : implementation("unsupported", "Unsupported CPU (no detected SIMD instructions)", 0) {} - }; +// So we can return UNSUPPORTED_ARCHITECTURE from the parser when there is no support +class unsupported_implementation final : public implementation { +public: + simdjson_warn_unused error_code create_dom_parser_implementation( + size_t, + size_t, + std::unique_ptr& + ) const noexcept final { + return UNSUPPORTED_ARCHITECTURE; + } + simdjson_warn_unused error_code minify(const uint8_t *, size_t, uint8_t *, size_t &) const noexcept final override { + return UNSUPPORTED_ARCHITECTURE; + } + simdjson_warn_unused bool validate_utf8(const char *, size_t) const noexcept final override { + return false; // Just refuse to validate. Given that we have a fallback implementation + // it seems unlikely that unsupported_implementation will ever be used. If it is used, + // then it will flag all strings as invalid. The alternative is to return an error_code + // from which the user has to figure out whether the string is valid UTF-8... which seems + // like a lot of work just to handle the very unlikely case that we have an unsupported + // implementation. And, when it does happen (that we have an unsupported implementation), + // what are the chances that the programmer has a fallback? Given that *we* provide the + // fallback, it implies that the programmer would need a fallback for our fallback. + } + unsupported_implementation() : implementation("unsupported", "Unsupported CPU (no detected SIMD instructions)", 0) {} +}; - static_assert(std::is_trivially_destructible::value, "unsupported_singleton should be trivially destructible"); +static_assert(std::is_trivially_destructible::value, "unsupported_singleton should be trivially destructible"); - const unsupported_implementation* get_unsupported_singleton() { - static const unsupported_implementation unsupported_singleton{}; - return &unsupported_singleton; - } +const unsupported_implementation* get_unsupported_singleton() { + static const unsupported_implementation unsupported_singleton{}; + return &unsupported_singleton; +} - size_t available_implementation_list::size() const noexcept { - return internal::get_available_implementation_pointers().size(); - } - const implementation* const* available_implementation_list::begin() const noexcept { - return internal::get_available_implementation_pointers().begin(); - } - const implementation* const* available_implementation_list::end() const noexcept { - return internal::get_available_implementation_pointers().end(); - } - const implementation* available_implementation_list::detect_best_supported() const noexcept { - // They are prelisted in priority order, so we just go down the list - uint32_t supported_instruction_sets = internal::detect_supported_architectures(); - for (const implementation* impl : internal::get_available_implementation_pointers()) { - uint32_t required_instruction_sets = impl->required_instruction_sets(); - if ((supported_instruction_sets & required_instruction_sets) == required_instruction_sets) { return impl; } - } - return get_unsupported_singleton(); // this should never happen? - } +size_t available_implementation_list::size() const noexcept { + return internal::get_available_implementation_pointers().size(); +} +const implementation * const *available_implementation_list::begin() const noexcept { + return internal::get_available_implementation_pointers().begin(); +} +const implementation * const *available_implementation_list::end() const noexcept { + return internal::get_available_implementation_pointers().end(); +} +const implementation *available_implementation_list::detect_best_supported() const noexcept { + // They are prelisted in priority order, so we just go down the list + uint32_t supported_instruction_sets = internal::detect_supported_architectures(); + for (const implementation *impl : internal::get_available_implementation_pointers()) { + uint32_t required_instruction_sets = impl->required_instruction_sets(); + if ((supported_instruction_sets & required_instruction_sets) == required_instruction_sets) { return impl; } + } + return get_unsupported_singleton(); // this should never happen? +} - const implementation* detect_best_supported_implementation_on_first_use::set_best() const noexcept { - SIMDJSON_PUSH_DISABLE_WARNINGS - SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe - char* force_implementation_name = getenv("SIMDJSON_FORCE_IMPLEMENTATION"); - SIMDJSON_POP_DISABLE_WARNINGS +const implementation *detect_best_supported_implementation_on_first_use::set_best() const noexcept { + SIMDJSON_PUSH_DISABLE_WARNINGS + SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe + char *force_implementation_name = getenv("SIMDJSON_FORCE_IMPLEMENTATION"); + SIMDJSON_POP_DISABLE_WARNINGS - if (force_implementation_name) { - auto force_implementation = get_available_implementations()[force_implementation_name]; - if (force_implementation) { - return get_active_implementation() = force_implementation; - } - else { - // Note: abort() and stderr usage within the library is forbidden. - return get_active_implementation() = get_unsupported_singleton(); - } - } - return get_active_implementation() = get_available_implementations().detect_best_supported(); - } - - } // namespace internal - - SIMDJSON_DLLIMPORTEXPORT const internal::available_implementation_list& get_available_implementations() { - static const internal::available_implementation_list available_implementations{}; - return available_implementations; + if (force_implementation_name) { + auto force_implementation = get_available_implementations()[force_implementation_name]; + if (force_implementation) { + return get_active_implementation() = force_implementation; + } else { + // Note: abort() and stderr usage within the library is forbidden. + return get_active_implementation() = get_unsupported_singleton(); } + } + return get_active_implementation() = get_available_implementations().detect_best_supported(); +} - SIMDJSON_DLLIMPORTEXPORT internal::atomic_ptr& get_active_implementation() { +} // namespace internal + +SIMDJSON_DLLIMPORTEXPORT const internal::available_implementation_list& get_available_implementations() { + static const internal::available_implementation_list available_implementations{}; + return available_implementations; +} + +SIMDJSON_DLLIMPORTEXPORT internal::atomic_ptr& get_active_implementation() { #if SIMDJSON_SINGLE_IMPLEMENTATION - // We immediately select the only implementation we have, skipping the - // detect_best_supported_implementation_on_first_use_singleton. - static internal::atomic_ptr active_implementation{ internal::get_single_implementation() }; - return active_implementation; + // We immediately select the only implementation we have, skipping the + // detect_best_supported_implementation_on_first_use_singleton. + static internal::atomic_ptr active_implementation{internal::get_single_implementation()}; + return active_implementation; #else - static const internal::detect_best_supported_implementation_on_first_use detect_best_supported_implementation_on_first_use_singleton; - static internal::atomic_ptr active_implementation{ &detect_best_supported_implementation_on_first_use_singleton }; - return active_implementation; + static const internal::detect_best_supported_implementation_on_first_use detect_best_supported_implementation_on_first_use_singleton; + static internal::atomic_ptr active_implementation{&detect_best_supported_implementation_on_first_use_singleton}; + return active_implementation; #endif - } +} - simdjson_warn_unused error_code minify(const char* buf, size_t len, char* dst, size_t& dst_len) noexcept { - return get_active_implementation()->minify(reinterpret_cast(buf), len, reinterpret_cast(dst), dst_len); - } - simdjson_warn_unused bool validate_utf8(const char* buf, size_t len) noexcept { - return get_active_implementation()->validate_utf8(buf, len); - } - const implementation* builtin_implementation() { - static const implementation* builtin_impl = get_available_implementations()[SIMDJSON_STRINGIFY(SIMDJSON_BUILTIN_IMPLEMENTATION)]; - assert(builtin_impl); - return builtin_impl; - } +simdjson_warn_unused error_code minify(const char *buf, size_t len, char *dst, size_t &dst_len) noexcept { + return get_active_implementation()->minify(reinterpret_cast(buf), len, reinterpret_cast(dst), dst_len); +} +simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) noexcept { + return get_active_implementation()->validate_utf8(buf, len); +} +const implementation * builtin_implementation() { + static const implementation * builtin_impl = get_available_implementations()[SIMDJSON_STRINGIFY(SIMDJSON_BUILTIN_IMPLEMENTATION)]; + assert(builtin_impl); + return builtin_impl; +} } // namespace simdjson @@ -7681,21 +8853,21 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - /** - * Implementation for NEON (ARMv8). - */ - namespace arm64 { +/** + * Implementation for NEON (ARMv8). + */ +namespace arm64 { - class implementation; +class implementation; - namespace { - namespace simd { - template struct simd8; - template struct simd8x64; - } // namespace simd - } // unnamed namespace +namespace { +namespace simd { +template struct simd8; +template struct simd8x64; +} // namespace simd +} // unnamed namespace - } // namespace arm64 +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_ARM64_BASE_H @@ -7728,100 +8900,106 @@ static_assert(sizeof(uint8x16_t) <= simdjson::SIMDJSON_PADDING, "insufficient pa /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { - namespace { +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) { -#ifdef 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; +// 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); + 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 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) { -#ifdef 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; +// 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); + 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) { - return vaddv_u8(vcnt_u8(vcreate_u8(input_num))); - } +/* result might be undefined when input_num is zero */ +simdjson_inline int count_ones(uint64_t input_num) { + return vaddv_u8(vcnt_u8(vcreate_u8(input_num))); +} #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. - */ +/** + * 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; - } +/* 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); - } +/** + * 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 - simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t* result) { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - * result = value1 + value2; - return *result < value1; +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t *result) { +#if SIMDJSON_REGULAR_VISUAL_STUDIO + *result = value1 + value2; + return *result < value1; #else - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); #endif - } +} - } // unnamed namespace - } // namespace arm64 +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_ARM64_BITMANIPULATION_H @@ -7836,39 +9014,39 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { - namespace { +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; - } +// +// 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 +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif @@ -7892,43 +9070,49 @@ namespace simdjson { #endif // SIMDJSON_REGULAR_VISUAL_STUDIO && SIMDJSON_IS_ARM64 namespace simdjson { - namespace arm64 { - namespace numberparsing { +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); - } +// 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; +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; + // 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 + 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); + __uint128_t r = (static_cast<__uint128_t>(value1)) * value2; + answer.low = uint64_t(r); + answer.high = uint64_t(r >> 64); #endif - return answer; - } + return answer; +} - } // namespace numberparsing - } // namespace arm64 +} // 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 */ @@ -7944,13 +9128,13 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { - namespace { - namespace simd { +namespace arm64 { +namespace { +namespace simd { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - namespace { - // Start of private section with Visual Studio workaround +#if SIMDJSON_REGULAR_VISUAL_STUDIO +namespace { +// Start of private section with Visual Studio workaround #ifndef simdjson_make_uint8x16_t @@ -8002,432 +9186,444 @@ namespace simdjson { #endif // End of private section with Visual Studio workaround - } // namespace +} // namespace #endif // SIMDJSON_REGULAR_VISUAL_STUDIO - template - struct simd8; + template + struct simd8; - // - // Base class of simd8 and simd8, both of which use uint8x16_t internally. - // - template> - struct base_u8 { - uint8x16_t value; - static const int SIZE = sizeof(value); + // + // Base class of simd8 and simd8, both of which use uint8x16_t internally. + // + template> + 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; } + // 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 operator|(const simd8 other) const { return vorrq_u8(*this, other); } - simdjson_inline simd8 operator&(const simd8 other) const { return vandq_u8(*this, other); } - simdjson_inline simd8 operator^(const simd8 other) const { return veorq_u8(*this, other); } - simdjson_inline simd8 bit_andnot(const simd8 other) const { return vbicq_u8(*this, other); } - simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - simdjson_inline simd8& operator|=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast | other; return *this_cast; } - simdjson_inline simd8& operator&=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast & other; return *this_cast; } - simdjson_inline simd8& operator^=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast ^ other; return *this_cast; } + // Bit operations + simdjson_inline simd8 operator|(const simd8 other) const { return vorrq_u8(*this, other); } + simdjson_inline simd8 operator&(const simd8 other) const { return vandq_u8(*this, other); } + simdjson_inline simd8 operator^(const simd8 other) const { return veorq_u8(*this, other); } + simdjson_inline simd8 bit_andnot(const simd8 other) const { return vbicq_u8(*this, other); } + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } + simdjson_inline simd8& operator|=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline simd8& operator&=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline simd8& operator^=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast ^ other; return *this_cast; } - friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return vceqq_u8(lhs, rhs); } + friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return vceqq_u8(lhs, rhs); } - template - simdjson_inline simd8 prev(const simd8 prev_chunk) const { - return vextq_u8(prev_chunk, *this, 16 - N); - } - }; + template + simdjson_inline simd8 prev(const simd8 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 : base_u8 { - typedef uint16_t bitmask_t; - typedef uint32_t bitmask2_t; + // SIMD byte mask type (returned by things like eq and gt) + template<> + struct simd8: base_u8 { + typedef uint16_t bitmask_t; + typedef uint32_t bitmask2_t; - static simdjson_inline simd8 splat(bool _value) { return vmovq_n_u8(uint8_t(-(!!_value))); } + static simdjson_inline simd8 splat(bool _value) { return vmovq_n_u8(uint8_t(-(!!_value))); } - simdjson_inline simd8(const uint8x16_t _value) : base_u8(_value) {} - // False constructor - simdjson_inline simd8() : simd8(vdupq_n_u8(0)) {} - // Splat constructor - simdjson_inline simd8(bool _value) : simd8(splat(_value)) {} + simdjson_inline simd8(const uint8x16_t _value) : base_u8(_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 { -#ifdef 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); + // 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 }; + 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); - } - simdjson_inline bool any() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) != 0; } - }; + 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); + } + simdjson_inline bool any() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) != 0; } + }; - // Unsigned bytes - template<> - struct simd8 : base_u8 { - 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); } + // Unsigned bytes + template<> + struct simd8: base_u8 { + 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(_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 -#ifdef 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 - )) {} + simdjson_inline simd8(const uint8x16_t _value) : base_u8(_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 - }) {} + 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 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( - 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 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( + 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); } + // Store to array + simdjson_inline void store(uint8_t dst[16]) const { return vst1q_u8(dst, *this); } - // Saturated math - simdjson_inline simd8 saturating_add(const simd8 other) const { return vqaddq_u8(*this, other); } - simdjson_inline simd8 saturating_sub(const simd8 other) const { return vqsubq_u8(*this, other); } + // Saturated math + simdjson_inline simd8 saturating_add(const simd8 other) const { return vqaddq_u8(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return vqsubq_u8(*this, other); } - // Addition/subtraction are the same for signed and unsigned - simdjson_inline simd8 operator+(const simd8 other) const { return vaddq_u8(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return vsubq_u8(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *this; } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *this; } + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { return vaddq_u8(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return vsubq_u8(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *this; } + simdjson_inline simd8& operator-=(const simd8 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 max_val(const simd8 other) const { return vmaxq_u8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return vminq_u8(*this, other); } - simdjson_inline simd8 operator<=(const simd8 other) const { return vcleq_u8(*this, other); } - simdjson_inline simd8 operator>=(const simd8 other) const { return vcgeq_u8(*this, other); } - simdjson_inline simd8 operator<(const simd8 other) const { return vcltq_u8(*this, other); } - simdjson_inline simd8 operator>(const simd8 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 gt_bits(const simd8 other) const { return simd8(*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 lt_bits(const simd8 other) const { return simd8(*this < other); } + // 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 max_val(const simd8 other) const { return vmaxq_u8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return vminq_u8(*this, other); } + simdjson_inline simd8 operator<=(const simd8 other) const { return vcleq_u8(*this, other); } + simdjson_inline simd8 operator>=(const simd8 other) const { return vcgeq_u8(*this, other); } + simdjson_inline simd8 operator<(const simd8 other) const { return vcltq_u8(*this, other); } + simdjson_inline simd8 operator>(const simd8 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 gt_bits(const simd8 other) const { return simd8(*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 lt_bits(const simd8 other) const { return simd8(*this < other); } - // Bit-specific operations - simdjson_inline simd8 any_bits_set(simd8 bits) const { return vtstq_u8(*this, bits); } - simdjson_inline bool any_bits_set_anywhere() const { return this->max_val() != 0; } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return (*this & bits).any_bits_set_anywhere(); } - template - simdjson_inline simd8 shr() const { return vshrq_n_u8(*this, N); } - template - simdjson_inline simd8 shl() const { return vshlq_n_u8(*this, N); } + // Bit-specific operations + simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return (*this & bits).any_bits_set_anywhere(); } + template + simdjson_inline simd8 shr() const { return vshrq_n_u8(*this, N); } + template + simdjson_inline simd8 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 - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return lookup_table.apply_lookup_16_to(*this); - } + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 lookup_table) const { + return lookup_table.apply_lookup_16_to(*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 compress(uint16_t mask) would be - // sensible, but the AVX ISA makes this kind of approach difficult. - template - 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 -#ifdef 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); + // 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 compress(uint16_t mask) would be + // sensible, but the AVX ISA makes this kind of approach difficult. + template + 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 }; + 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(pshufb_combine_table + pop1 * 8)); - uint8x16_t answer = vqtbl1q_u8(pruned, compactmask); - vst1q_u8(reinterpret_cast(output), answer); - } + 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(pshufb_combine_table + pop1 * 8)); + uint8x16_t answer = vqtbl1q_u8(pruned, compactmask); + vst1q_u8(reinterpret_cast(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 - 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 -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - uint8x8_t inc = simdjson_make_uint8x8_t(0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08); + // 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 + 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 }; + 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)); - } + 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 - simdjson_inline simd8 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::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } - template - simdjson_inline simd8 apply_lookup_16_to(const simd8 original) { - return vqtbl1q_u8(*this, simd8(original)); - } - }; + template + simdjson_inline simd8 apply_lookup_16_to(const simd8 original) { + return vqtbl1q_u8(*this, simd8(original)); + } + }; - // Signed bytes - template<> - struct simd8 { - int8x16_t value; + // Signed bytes + template<> + struct simd8 { + int8x16_t value; - static simdjson_inline simd8 splat(int8_t _value) { return vmovq_n_s8(_value); } - static simdjson_inline simd8 zero() { return vdupq_n_s8(0); } - static simdjson_inline simd8 load(const int8_t values[16]) { return vld1q_s8(values); } + static simdjson_inline simd8 splat(int8_t _value) { return vmovq_n_s8(_value); } + static simdjson_inline simd8 zero() { return vdupq_n_s8(0); } + static simdjson_inline simd8 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; } + // 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 -#ifdef 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 - )) {} + // 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 - }) {} + 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 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( - 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 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( + 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); } + // 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. + // 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)) {} + simdjson_inline explicit simd8(const uint8x16_t other): simd8(vreinterpretq_s8_u8(other)) {} #endif - simdjson_inline explicit operator simd8() const { return vreinterpretq_u8_s8(this->value); } + simdjson_inline explicit operator simd8() const { return vreinterpretq_u8_s8(this->value); } - // Math - simdjson_inline simd8 operator+(const simd8 other) const { return vaddq_s8(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return vsubq_s8(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *this; } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *this; } + // Math + simdjson_inline simd8 operator+(const simd8 other) const { return vaddq_s8(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return vsubq_s8(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *this; } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *this; } - // Order-sensitive comparisons - simdjson_inline simd8 max_val(const simd8 other) const { return vmaxq_s8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return vminq_s8(*this, other); } - simdjson_inline simd8 operator>(const simd8 other) const { return vcgtq_s8(*this, other); } - simdjson_inline simd8 operator<(const simd8 other) const { return vcltq_s8(*this, other); } - simdjson_inline simd8 operator==(const simd8 other) const { return vceqq_s8(*this, other); } + // Order-sensitive comparisons + simdjson_inline simd8 max_val(const simd8 other) const { return vmaxq_s8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return vminq_s8(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return vcgtq_s8(*this, other); } + simdjson_inline simd8 operator<(const simd8 other) const { return vcltq_s8(*this, other); } + simdjson_inline simd8 operator==(const simd8 other) const { return vceqq_s8(*this, other); } - template - simdjson_inline simd8 prev(const simd8 prev_chunk) const { - return vextq_s8(prev_chunk, *this, 16 - N); - } + template + simdjson_inline simd8 prev(const simd8 prev_chunk) const { + return vextq_s8(prev_chunk, *this, 16 - N); + } - // Perform a lookup assuming no value is larger than 16 - template - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return lookup_table.apply_lookup_16_to(*this); - } - template - simdjson_inline simd8 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::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } + // Perform a lookup assuming no value is larger than 16 + template + simdjson_inline simd8 lookup_16(simd8 lookup_table) const { + return lookup_table.apply_lookup_16_to(*this); + } + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } - template - simdjson_inline simd8 apply_lookup_16_to(const simd8 original) { - return vqtbl1q_s8(*this, simd8(original)); - } - }; + template + simdjson_inline simd8 apply_lookup_16_to(const simd8 original) { + return vqtbl1q_s8(*this, simd8(original)); + } + }; - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 4, "ARM kernel should use four registers per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 4, "ARM kernel should use four registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{ chunk0, chunk1, chunk2, chunk3 } {} - simdjson_inline simd8x64(const T ptr[64]) : chunks{ simd8::load(ptr), simd8::load(ptr + 16), simd8::load(ptr + 32), simd8::load(ptr + 48) } {} + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::load(ptr+16), simd8::load(ptr+32), simd8::load(ptr+48)} {} - simdjson_inline void store(T ptr[64]) const { - this->chunks[0].store(ptr + sizeof(simd8) * 0); - this->chunks[1].store(ptr + sizeof(simd8) * 1); - this->chunks[2].store(ptr + sizeof(simd8) * 2); - this->chunks[3].store(ptr + sizeof(simd8) * 3); - } + simdjson_inline void store(T ptr[64]) const { + this->chunks[0].store(ptr+sizeof(simd8)*0); + this->chunks[1].store(ptr+sizeof(simd8)*1); + this->chunks[2].store(ptr+sizeof(simd8)*2); + this->chunks[3].store(ptr+sizeof(simd8)*3); + } - simdjson_inline simd8 reduce_or() const { - return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]); - } + simdjson_inline simd8 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 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 { -#ifdef 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 - ); + 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 - }; + 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); - } + // 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] == mask, - this->chunks[1] == mask, - this->chunks[2] == mask, - this->chunks[3] == mask - ).to_bitmask(); - } + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] <= mask, - this->chunks[1] <= mask, - this->chunks[2] <= mask, - this->chunks[3] <= mask - ).to_bitmask(); - } - }; // struct simd8x64 + simdjson_inline uint64_t lteq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] <= mask, + this->chunks[1] <= mask, + this->chunks[2] <= mask, + this->chunks[3] <= mask + ).to_bitmask(); + } + }; // struct simd8x64 - } // namespace simd - } // unnamed namespace - } // namespace arm64 +} // namespace simd +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_ARM64_SIMD_H @@ -8444,46 +9640,72 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { - namespace { +namespace arm64 { +namespace { - using namespace simd; +using namespace simd; - // Holds backslashes and quotes locations. - struct backslash_and_quote { - public: - static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t* src, uint8_t* dst); +// 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); } + 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 + 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 v0(src); - simd8 v1(src + sizeof(v0)); - v0.store(dst); - v1.store(dst + sizeof(v0)); +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 v0(src); + simd8 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(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); - return { - uint32_t(bs_and_quote), // bs_bits - uint32_t(bs_and_quote >> 32) // quote_bits - }; - } + // 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(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); + return { + uint32_t(bs_and_quote), // bs_bits + uint32_t(bs_and_quote >> 32) // quote_bits + }; +} - } // unnamed namespace - } // namespace arm64 +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 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 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 @@ -8518,10 +9740,12 @@ namespace simdjson { /* 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_LSX */ -/* amalgamation skipped (editor-only): #include "simdjson/lsx/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 */ @@ -8531,22 +9755,22 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { +namespace arm64 { - struct open_container; - class dom_parser_implementation; +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 - }; +/** + * 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 arm64 } // namespace simdjson #endif // SIMDJSON_GENERIC_BASE_H @@ -8563,99 +9787,97 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { - namespace { - namespace jsoncharutils { +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]; - } +// 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]; - } +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; - } +// 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 - } +// 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; - } +// 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 jsoncharutils +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_GENERIC_JSONCHARUTILS_H @@ -8673,69 +9895,69 @@ namespace simdjson { #include namespace simdjson { - namespace arm64 { - namespace { - /// @private - namespace atomparsing { +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; } +// 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; } - // 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); - } +// 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); +} - 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) { + 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_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) { + 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_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) { + 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; } - } +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; } +} - } // namespace atomparsing - } // unnamed namespace - } // namespace arm64 +} // namespace atomparsing +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_GENERIC_ATOMPARSING_H @@ -8751,83 +9973,83 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { +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 +// 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"); +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_containers{}; - /** Whether each open container is a [ or { */ - std::unique_ptr 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{}; +class dom_parser_implementation final : public internal::dom_parser_implementation { +public: + /** Tape location of each open { or [ */ + std::unique_ptr open_containers{}; + /** Whether each open container is a [ or { */ + std::unique_ptr 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; + 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); + 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 arm64 } // namespace simdjson namespace simdjson { - namespace arm64 { +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; +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) { return CAPACITY; } - // Stage 1 index output - size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; - 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; +// 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) { return CAPACITY; } + // Stage 1 index output + size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; + 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; - } + _capacity = capacity; + return SUCCESS; +} - inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { - // 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; } +inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { + // 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; - } + _max_depth = max_depth; + return SUCCESS; +} - } // namespace arm64 +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H @@ -8842,129 +10064,148 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { +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 : public internal::implementation_simdjson_result_base { - * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} - * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} - * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} - * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} - * // Your extra methods here - * } - * - * Then any method returning simdjson_result will be chainable with your methods. - */ - template - struct implementation_simdjson_result_base { +// 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 : public internal::implementation_simdjson_result_base { + * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} + * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} + * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} + * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} + * // Your extra methods here + * } + * + * Then any method returning simdjson_result will be chainable with your methods. + */ +template +struct implementation_simdjson_result_base { - /** - * Create a new empty result with error = UNINITIALIZED. - */ - simdjson_inline implementation_simdjson_result_base() noexcept = default; + /** + * 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 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 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; + /** + * 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 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; + /** + * 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_inline error_code error() const 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& value() & noexcept(false); + /** + * 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); - /** - * Take the result value (move it). - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline T&& value() && 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&& take_value() && noexcept(false); + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& 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); + /** + * 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; - 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 + /** + * 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; - } // namespace arm64 + 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 @@ -8985,8 +10226,8 @@ namespace simdjson { #include namespace simdjson { - namespace arm64 { - namespace numberparsing { +namespace arm64 { +namespace numberparsing { #ifdef JSON_TEST_NUMBERS #define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) @@ -9002,540 +10243,547 @@ namespace simdjson { #define BIGINT_NUMBER(SRC) (BIGINT_ERROR) #endif - namespace { +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(negative)) << 63); - std::memcpy(&d, &mantissa, sizeof(d)); - return d; - } +// 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(negative)) << 63); + std::memcpy(&d, &mantissa, sizeof(d)); + return d; +} - // 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 +// 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) + if (0 <= power && power <= 22 && i <= 9007199254740991) #else - if (-22 <= power && power <= 22 && i <= 9007199254740991) + 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 + { + // 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. + // 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; - } + // 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 - // approximatively 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; + // 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 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. - simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]); - // 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. + // 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 - // 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. - simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); - 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); + // 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. - // 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 - } - } + // 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); - mantissa += mantissa & 1; - mantissa >>= 1; + // 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 + } + } - // 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; - } + mantissa += mantissa & 1; + mantissa >>= 1; - // 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(ptr)); - // We do not accept infinite values. + // 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; +} - // 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::max)() || *outDouble < std::numeric_limits::lowest()); - } +// 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(ptr)); + // We do not accept infinite values. - static bool parse_float_fallback(const uint8_t* ptr, const uint8_t* end_ptr, double* outDouble) { - *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(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::max)() || *outDouble < std::numeric_limits::lowest()); +} - // 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::max)() || *outDouble < std::numeric_limits::lowest()); - } +static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) { + *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(end_ptr)); + // We do not accept infinite values. - // 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); - } + // 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::max)() || *outDouble < std::numeric_limits::lowest()); +} - template - 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(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; - } +// 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); +} - simdjson_inline bool is_digit(const uint8_t c) { - return static_cast(c - '0') <= 9; - } +template +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(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 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; +simdjson_inline bool is_digit(const uint8_t c) { + return static_cast(c - '0') <= 9; +} + +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; #ifdef SIMDJSON_SWAR_NUMBER_PARSING #if SIMDJSON_SWAR_NUMBER_PARSING - // this helps if we have lots of decimals! - // this turns out to be frequent enough. - if (is_made_of_eight_digits_fast(p)) { - i = i * 100000000 + parse_eight_digits_unrolled(p); - p += 8; - } + // this helps if we have lots of decimals! + // this turns out to be frequent enough. + if (is_made_of_eight_digits_fast(p)) { + i = i * 100000000 + parse_eight_digits_unrolled(p); + p += 8; + } #endif // SIMDJSON_SWAR_NUMBER_PARSING #endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING - // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) - if (parse_digit(*p, i)) { ++p; } - while (parse_digit(*p, i)) { p++; } - exponent = first_after_period - p; - // Decimal without digits (123.) is illegal - if (exponent == 0) { - return INVALID_NUMBER(src); - } - return SUCCESS; - } + // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) + if (parse_digit(*p, i)) { ++p; } + while (parse_digit(*p, i)) { p++; } + exponent = first_after_period - p; + // Decimal without digits (123.) is illegal + if (exponent == 0) { + return INVALID_NUMBER(src); + } + return SUCCESS; +} - 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 +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. + // 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. + // 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 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; - } + // 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 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); - } +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 +} // unnamed namespace - /** @private */ - static 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 */ +static 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 - 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; - } +/** @private */ +template +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 - simdjson_inline error_code parse_number(const uint8_t* const src, W& writer); +// 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 +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 +// for performance analysis, it is sometimes useful to skip parsing #ifdef SIMDJSON_SKIPNUMBERPARSING - template - simdjson_inline error_code parse_number(const uint8_t* const, W& writer) { - writer.append_s64(0); // always write zero - return SUCCESS; // always succeeds - } +template +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 parse_unsigned(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline bool is_negative(const uint8_t* src) noexcept { return false; } - simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t* src) noexcept { return false; } - simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t* src) noexcept { return number_type::signed_integer; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t * src) noexcept { return number_type::signed_integer; } #else // parse the number at src @@ -9547,744 +10795,753 @@ namespace simdjson { // content and append a space before calling this function. // // Our objective is accurate parsing (ULP of 0) at high speed. - template - 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)) { 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 INVALID_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 { - WRITE_INTEGER(negative ? (~i + 1) : i, src, writer); - } - 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 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; - 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); - // 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 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 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; - 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); - // 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 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; - 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); - // 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 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 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; - while (parse_digit(*src, i)) { src++; } - - // 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 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; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (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 == '.')) { - p++; - const uint8_t* start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (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 = 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; - } - - simdjson_unused simdjson_inline bool is_negative(const uint8_t* src) noexcept { - return (*src == '-'); - } - - simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t* src) noexcept { - bool negative = (*src == '-'); - src += uint8_t(negative); - const uint8_t* p = src; - while (static_cast(*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 get_number_type(const uint8_t* src) noexcept { - bool negative = (*src == '-'); - src += uint8_t(negative); - const uint8_t* p = src; - while (static_cast(*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("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; - } - 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("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 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 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; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (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 == '.')) { - p++; - const uint8_t* start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (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 = 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; - } - - } // unnamed namespace +template +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)) { 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 INVALID_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 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; + 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); + // 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 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 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; + 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); + // 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 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; + 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); + // 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 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 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; + while (parse_digit(*src, i)) { src++; } + + // 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { + return (*src == '-'); +} + +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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 get_number_type(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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("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("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 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +} // unnamed namespace #endif // SIMDJSON_SKIPNUMBERPARSING - } // namespace numberparsing +} // 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; - } +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 arm64 } // namespace simdjson #endif // SIMDJSON_GENERIC_NUMBERPARSING_H @@ -10301,84 +11558,113 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { +namespace arm64 { - // - // internal::implementation_simdjson_result_base inline implementation - // +// +// internal::implementation_simdjson_result_base inline implementation +// - template - simdjson_inline void implementation_simdjson_result_base::tie(T& value, error_code& error) && noexcept { - error = this->second; - if (!error) { - value = std::forward>(*this).first; - } - } +template +simdjson_inline void implementation_simdjson_result_base::tie(T &value, error_code &error) && noexcept { + error = this->second; + if (!error) { + value = std::forward>(*this).first; + } +} - template - simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T& value) && noexcept { - error_code error; - std::forward>(*this).tie(value, error); - return error; - } +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T &value) && noexcept { + error_code error; + std::forward>(*this).tie(value, error); + return error; +} - template - simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { - return this->second; - } +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { + return this->second; +} + + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} #if SIMDJSON_EXCEPTIONS - template - simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return this->first; - } +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} - template - simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { - return std::forward>(*this).take_value(); - } +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} - template - simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return std::forward(this->first); - } +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} - template - simdjson_inline implementation_simdjson_result_base::operator T && () && noexcept(false) { - return std::forward>(*this).take_value(); - } + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + +template +simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return this->first; +} + +template +simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { + return std::forward>(*this).take_value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return std::forward(this->first); +} + +template +simdjson_inline implementation_simdjson_result_base::operator T&&() && noexcept(false) { + return std::forward>(*this).take_value(); +} #endif // SIMDJSON_EXCEPTIONS - template - simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { - return this->first; - } +template +simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { + return this->first; +} - template - simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { - return this->first; - } +template +simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { + return this->first; +} - template - simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { - return std::forward(this->first); - } +template +simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { + return std::forward(this->first); +} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T&& value, error_code error) noexcept - : first{ std::forward(value) }, second{ error } {} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept - : implementation_simdjson_result_base(T{}, error) {} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T&& value) noexcept - : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value, error_code error) noexcept + : first{std::forward(value)}, second{error} {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept + : implementation_simdjson_result_base(T{}, error) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value) noexcept + : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} - } // namespace arm64 +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H @@ -10409,24 +11695,24 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { +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& 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; - }; +/** + * @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& 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 arm64 } // namespace simdjson #endif // SIMDJSON_ARM64_IMPLEMENTATION_H @@ -10446,21 +11732,21 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - /** - * Implementation for NEON (ARMv8). - */ - namespace arm64 { +/** + * Implementation for NEON (ARMv8). + */ +namespace arm64 { - class implementation; +class implementation; - namespace { - namespace simd { - template struct simd8; - template struct simd8x64; - } // namespace simd - } // unnamed namespace +namespace { +namespace simd { +template struct simd8; +template struct simd8x64; +} // namespace simd +} // unnamed namespace - } // namespace arm64 +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_ARM64_BASE_H @@ -10493,100 +11779,106 @@ static_assert(sizeof(uint8x16_t) <= simdjson::SIMDJSON_PADDING, "insufficient pa /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { - namespace { +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) { -#ifdef 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; +// 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); + 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 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) { -#ifdef 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; +// 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); + 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) { - return vaddv_u8(vcnt_u8(vcreate_u8(input_num))); - } +/* result might be undefined when input_num is zero */ +simdjson_inline int count_ones(uint64_t input_num) { + return vaddv_u8(vcnt_u8(vcreate_u8(input_num))); +} #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. - */ +/** + * 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; - } +/* 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); - } +/** + * 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 - simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t* result) { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - * result = value1 + value2; - return *result < value1; +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t *result) { +#if SIMDJSON_REGULAR_VISUAL_STUDIO + *result = value1 + value2; + return *result < value1; #else - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); #endif - } +} - } // unnamed namespace - } // namespace arm64 +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_ARM64_BITMANIPULATION_H @@ -10601,39 +11893,39 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { - namespace { +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; - } +// +// 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 +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif @@ -10657,43 +11949,49 @@ namespace simdjson { #endif // SIMDJSON_REGULAR_VISUAL_STUDIO && SIMDJSON_IS_ARM64 namespace simdjson { - namespace arm64 { - namespace numberparsing { +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); - } +// 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; +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; + // 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 + 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); + __uint128_t r = (static_cast<__uint128_t>(value1)) * value2; + answer.low = uint64_t(r); + answer.high = uint64_t(r >> 64); #endif - return answer; - } + return answer; +} - } // namespace numberparsing - } // namespace arm64 +} // 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 */ @@ -10709,13 +12007,13 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { - namespace { - namespace simd { +namespace arm64 { +namespace { +namespace simd { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - namespace { - // Start of private section with Visual Studio workaround +#if SIMDJSON_REGULAR_VISUAL_STUDIO +namespace { +// Start of private section with Visual Studio workaround #ifndef simdjson_make_uint8x16_t @@ -10767,432 +12065,444 @@ namespace simdjson { #endif // End of private section with Visual Studio workaround - } // namespace +} // namespace #endif // SIMDJSON_REGULAR_VISUAL_STUDIO - template - struct simd8; + template + struct simd8; - // - // Base class of simd8 and simd8, both of which use uint8x16_t internally. - // - template> - struct base_u8 { - uint8x16_t value; - static const int SIZE = sizeof(value); + // + // Base class of simd8 and simd8, both of which use uint8x16_t internally. + // + template> + 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; } + // 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 operator|(const simd8 other) const { return vorrq_u8(*this, other); } - simdjson_inline simd8 operator&(const simd8 other) const { return vandq_u8(*this, other); } - simdjson_inline simd8 operator^(const simd8 other) const { return veorq_u8(*this, other); } - simdjson_inline simd8 bit_andnot(const simd8 other) const { return vbicq_u8(*this, other); } - simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - simdjson_inline simd8& operator|=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast | other; return *this_cast; } - simdjson_inline simd8& operator&=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast & other; return *this_cast; } - simdjson_inline simd8& operator^=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast ^ other; return *this_cast; } + // Bit operations + simdjson_inline simd8 operator|(const simd8 other) const { return vorrq_u8(*this, other); } + simdjson_inline simd8 operator&(const simd8 other) const { return vandq_u8(*this, other); } + simdjson_inline simd8 operator^(const simd8 other) const { return veorq_u8(*this, other); } + simdjson_inline simd8 bit_andnot(const simd8 other) const { return vbicq_u8(*this, other); } + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } + simdjson_inline simd8& operator|=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline simd8& operator&=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline simd8& operator^=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast ^ other; return *this_cast; } - friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return vceqq_u8(lhs, rhs); } + friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return vceqq_u8(lhs, rhs); } - template - simdjson_inline simd8 prev(const simd8 prev_chunk) const { - return vextq_u8(prev_chunk, *this, 16 - N); - } - }; + template + simdjson_inline simd8 prev(const simd8 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 : base_u8 { - typedef uint16_t bitmask_t; - typedef uint32_t bitmask2_t; + // SIMD byte mask type (returned by things like eq and gt) + template<> + struct simd8: base_u8 { + typedef uint16_t bitmask_t; + typedef uint32_t bitmask2_t; - static simdjson_inline simd8 splat(bool _value) { return vmovq_n_u8(uint8_t(-(!!_value))); } + static simdjson_inline simd8 splat(bool _value) { return vmovq_n_u8(uint8_t(-(!!_value))); } - simdjson_inline simd8(const uint8x16_t _value) : base_u8(_value) {} - // False constructor - simdjson_inline simd8() : simd8(vdupq_n_u8(0)) {} - // Splat constructor - simdjson_inline simd8(bool _value) : simd8(splat(_value)) {} + simdjson_inline simd8(const uint8x16_t _value) : base_u8(_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 { -#ifdef 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); + // 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 }; + 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); - } - simdjson_inline bool any() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) != 0; } - }; + 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); + } + simdjson_inline bool any() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) != 0; } + }; - // Unsigned bytes - template<> - struct simd8 : base_u8 { - 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); } + // Unsigned bytes + template<> + struct simd8: base_u8 { + 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(_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 -#ifdef 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 - )) {} + simdjson_inline simd8(const uint8x16_t _value) : base_u8(_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 - }) {} + 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 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( - 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 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( + 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); } + // Store to array + simdjson_inline void store(uint8_t dst[16]) const { return vst1q_u8(dst, *this); } - // Saturated math - simdjson_inline simd8 saturating_add(const simd8 other) const { return vqaddq_u8(*this, other); } - simdjson_inline simd8 saturating_sub(const simd8 other) const { return vqsubq_u8(*this, other); } + // Saturated math + simdjson_inline simd8 saturating_add(const simd8 other) const { return vqaddq_u8(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return vqsubq_u8(*this, other); } - // Addition/subtraction are the same for signed and unsigned - simdjson_inline simd8 operator+(const simd8 other) const { return vaddq_u8(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return vsubq_u8(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *this; } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *this; } + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { return vaddq_u8(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return vsubq_u8(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *this; } + simdjson_inline simd8& operator-=(const simd8 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 max_val(const simd8 other) const { return vmaxq_u8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return vminq_u8(*this, other); } - simdjson_inline simd8 operator<=(const simd8 other) const { return vcleq_u8(*this, other); } - simdjson_inline simd8 operator>=(const simd8 other) const { return vcgeq_u8(*this, other); } - simdjson_inline simd8 operator<(const simd8 other) const { return vcltq_u8(*this, other); } - simdjson_inline simd8 operator>(const simd8 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 gt_bits(const simd8 other) const { return simd8(*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 lt_bits(const simd8 other) const { return simd8(*this < other); } + // 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 max_val(const simd8 other) const { return vmaxq_u8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return vminq_u8(*this, other); } + simdjson_inline simd8 operator<=(const simd8 other) const { return vcleq_u8(*this, other); } + simdjson_inline simd8 operator>=(const simd8 other) const { return vcgeq_u8(*this, other); } + simdjson_inline simd8 operator<(const simd8 other) const { return vcltq_u8(*this, other); } + simdjson_inline simd8 operator>(const simd8 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 gt_bits(const simd8 other) const { return simd8(*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 lt_bits(const simd8 other) const { return simd8(*this < other); } - // Bit-specific operations - simdjson_inline simd8 any_bits_set(simd8 bits) const { return vtstq_u8(*this, bits); } - simdjson_inline bool any_bits_set_anywhere() const { return this->max_val() != 0; } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return (*this & bits).any_bits_set_anywhere(); } - template - simdjson_inline simd8 shr() const { return vshrq_n_u8(*this, N); } - template - simdjson_inline simd8 shl() const { return vshlq_n_u8(*this, N); } + // Bit-specific operations + simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return (*this & bits).any_bits_set_anywhere(); } + template + simdjson_inline simd8 shr() const { return vshrq_n_u8(*this, N); } + template + simdjson_inline simd8 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 - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return lookup_table.apply_lookup_16_to(*this); - } + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 lookup_table) const { + return lookup_table.apply_lookup_16_to(*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 compress(uint16_t mask) would be - // sensible, but the AVX ISA makes this kind of approach difficult. - template - 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 -#ifdef 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); + // 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 compress(uint16_t mask) would be + // sensible, but the AVX ISA makes this kind of approach difficult. + template + 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 }; + 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(pshufb_combine_table + pop1 * 8)); - uint8x16_t answer = vqtbl1q_u8(pruned, compactmask); - vst1q_u8(reinterpret_cast(output), answer); - } + 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(pshufb_combine_table + pop1 * 8)); + uint8x16_t answer = vqtbl1q_u8(pruned, compactmask); + vst1q_u8(reinterpret_cast(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 - 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 -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - uint8x8_t inc = simdjson_make_uint8x8_t(0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08); + // 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 + 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 }; + 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)); - } + 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 - simdjson_inline simd8 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::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } - template - simdjson_inline simd8 apply_lookup_16_to(const simd8 original) { - return vqtbl1q_u8(*this, simd8(original)); - } - }; + template + simdjson_inline simd8 apply_lookup_16_to(const simd8 original) { + return vqtbl1q_u8(*this, simd8(original)); + } + }; - // Signed bytes - template<> - struct simd8 { - int8x16_t value; + // Signed bytes + template<> + struct simd8 { + int8x16_t value; - static simdjson_inline simd8 splat(int8_t _value) { return vmovq_n_s8(_value); } - static simdjson_inline simd8 zero() { return vdupq_n_s8(0); } - static simdjson_inline simd8 load(const int8_t values[16]) { return vld1q_s8(values); } + static simdjson_inline simd8 splat(int8_t _value) { return vmovq_n_s8(_value); } + static simdjson_inline simd8 zero() { return vdupq_n_s8(0); } + static simdjson_inline simd8 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; } + // 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 -#ifdef 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 - )) {} + // 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 - }) {} + 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 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( - 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 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( + 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); } + // 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. + // 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)) {} + simdjson_inline explicit simd8(const uint8x16_t other): simd8(vreinterpretq_s8_u8(other)) {} #endif - simdjson_inline explicit operator simd8() const { return vreinterpretq_u8_s8(this->value); } + simdjson_inline explicit operator simd8() const { return vreinterpretq_u8_s8(this->value); } - // Math - simdjson_inline simd8 operator+(const simd8 other) const { return vaddq_s8(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return vsubq_s8(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *this; } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *this; } + // Math + simdjson_inline simd8 operator+(const simd8 other) const { return vaddq_s8(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return vsubq_s8(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *this; } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *this; } - // Order-sensitive comparisons - simdjson_inline simd8 max_val(const simd8 other) const { return vmaxq_s8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return vminq_s8(*this, other); } - simdjson_inline simd8 operator>(const simd8 other) const { return vcgtq_s8(*this, other); } - simdjson_inline simd8 operator<(const simd8 other) const { return vcltq_s8(*this, other); } - simdjson_inline simd8 operator==(const simd8 other) const { return vceqq_s8(*this, other); } + // Order-sensitive comparisons + simdjson_inline simd8 max_val(const simd8 other) const { return vmaxq_s8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return vminq_s8(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return vcgtq_s8(*this, other); } + simdjson_inline simd8 operator<(const simd8 other) const { return vcltq_s8(*this, other); } + simdjson_inline simd8 operator==(const simd8 other) const { return vceqq_s8(*this, other); } - template - simdjson_inline simd8 prev(const simd8 prev_chunk) const { - return vextq_s8(prev_chunk, *this, 16 - N); - } + template + simdjson_inline simd8 prev(const simd8 prev_chunk) const { + return vextq_s8(prev_chunk, *this, 16 - N); + } - // Perform a lookup assuming no value is larger than 16 - template - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return lookup_table.apply_lookup_16_to(*this); - } - template - simdjson_inline simd8 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::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } + // Perform a lookup assuming no value is larger than 16 + template + simdjson_inline simd8 lookup_16(simd8 lookup_table) const { + return lookup_table.apply_lookup_16_to(*this); + } + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } - template - simdjson_inline simd8 apply_lookup_16_to(const simd8 original) { - return vqtbl1q_s8(*this, simd8(original)); - } - }; + template + simdjson_inline simd8 apply_lookup_16_to(const simd8 original) { + return vqtbl1q_s8(*this, simd8(original)); + } + }; - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 4, "ARM kernel should use four registers per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 4, "ARM kernel should use four registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{ chunk0, chunk1, chunk2, chunk3 } {} - simdjson_inline simd8x64(const T ptr[64]) : chunks{ simd8::load(ptr), simd8::load(ptr + 16), simd8::load(ptr + 32), simd8::load(ptr + 48) } {} + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::load(ptr+16), simd8::load(ptr+32), simd8::load(ptr+48)} {} - simdjson_inline void store(T ptr[64]) const { - this->chunks[0].store(ptr + sizeof(simd8) * 0); - this->chunks[1].store(ptr + sizeof(simd8) * 1); - this->chunks[2].store(ptr + sizeof(simd8) * 2); - this->chunks[3].store(ptr + sizeof(simd8) * 3); - } + simdjson_inline void store(T ptr[64]) const { + this->chunks[0].store(ptr+sizeof(simd8)*0); + this->chunks[1].store(ptr+sizeof(simd8)*1); + this->chunks[2].store(ptr+sizeof(simd8)*2); + this->chunks[3].store(ptr+sizeof(simd8)*3); + } - simdjson_inline simd8 reduce_or() const { - return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]); - } + simdjson_inline simd8 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 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 { -#ifdef 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 - ); + 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 - }; + 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); - } + // 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] == mask, - this->chunks[1] == mask, - this->chunks[2] == mask, - this->chunks[3] == mask - ).to_bitmask(); - } + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] <= mask, - this->chunks[1] <= mask, - this->chunks[2] <= mask, - this->chunks[3] <= mask - ).to_bitmask(); - } - }; // struct simd8x64 + simdjson_inline uint64_t lteq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] <= mask, + this->chunks[1] <= mask, + this->chunks[2] <= mask, + this->chunks[3] <= mask + ).to_bitmask(); + } + }; // struct simd8x64 - } // namespace simd - } // unnamed namespace - } // namespace arm64 +} // namespace simd +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_ARM64_SIMD_H @@ -11209,46 +12519,72 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { - namespace { +namespace arm64 { +namespace { - using namespace simd; +using namespace simd; - // Holds backslashes and quotes locations. - struct backslash_and_quote { - public: - static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t* src, uint8_t* dst); +// 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); } + 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 + 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 v0(src); - simd8 v1(src + sizeof(v0)); - v0.store(dst); - v1.store(dst + sizeof(v0)); +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 v0(src); + simd8 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(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); - return { - uint32_t(bs_and_quote), // bs_bits - uint32_t(bs_and_quote >> 32) // quote_bits - }; - } + // 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(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); + return { + uint32_t(bs_and_quote), // bs_bits + uint32_t(bs_and_quote >> 32) // quote_bits + }; +} - } // unnamed namespace - } // namespace arm64 +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 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 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 @@ -11273,13 +12609,13 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { - namespace { +namespace arm64 { +namespace { - struct json_character_block; +struct json_character_block; - } // unnamed namespace - } // namespace arm64 +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_BASE_H @@ -11296,14 +12632,14 @@ namespace simdjson { // Interface a dom parser implementation must fulfill namespace simdjson { - namespace arm64 { - namespace { +namespace arm64 { +namespace { - simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3); - simdjson_inline bool is_ascii(const simd8x64& input); +simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3); +simdjson_inline bool is_ascii(const simd8x64& input); - } // unnamed namespace - } // namespace arm64 +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_DOM_PARSER_IMPLEMENTATION_H @@ -11318,22 +12654,22 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { - namespace { +namespace arm64 { +namespace { - struct json_character_block { - static simdjson_inline json_character_block classify(const simd::simd8x64& in); +struct json_character_block { + static simdjson_inline json_character_block classify(const simd::simd8x64& in); - simdjson_inline uint64_t whitespace() const noexcept { return _whitespace; } - simdjson_inline uint64_t op() const noexcept { return _op; } - simdjson_inline uint64_t scalar() const noexcept { return ~(op() | whitespace()); } + simdjson_inline uint64_t whitespace() const noexcept { return _whitespace; } + simdjson_inline uint64_t op() const noexcept { return _op; } + simdjson_inline uint64_t scalar() const noexcept { return ~(op() | whitespace()); } - uint64_t _whitespace; - uint64_t _op; - }; + uint64_t _whitespace; + uint64_t _op; +}; - } // unnamed namespace - } // namespace arm64 +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_JSON_CHARACTER_BLOCK_H @@ -11352,30 +12688,30 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { - namespace { - namespace stage1 { +namespace arm64 { +namespace { +namespace stage1 { - class bit_indexer; - template - struct buf_block_reader; - struct json_block; - class json_minifier; - class json_scanner; - struct json_string_block; - class json_string_scanner; - class json_structural_indexer; +class bit_indexer; +template +struct buf_block_reader; +struct json_block; +class json_minifier; +class json_scanner; +struct json_string_block; +class json_string_scanner; +class json_structural_indexer; - } // namespace stage1 +} // namespace stage1 - namespace utf8_validation { - struct utf8_checker; - } // namespace utf8_validation +namespace utf8_validation { +struct utf8_checker; +} // namespace utf8_validation - using utf8_validation::utf8_checker; +using utf8_validation::utf8_checker; - } // unnamed namespace - } // namespace arm64 +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_BASE_H @@ -11392,109 +12728,109 @@ namespace simdjson { #include namespace simdjson { - namespace arm64 { - namespace { - namespace stage1 { +namespace arm64 { +namespace { +namespace stage1 { - // Walks through a buffer in block-sized increments, loading the last part with spaces - template - struct buf_block_reader { - public: - simdjson_inline buf_block_reader(const uint8_t* _buf, size_t _len); - simdjson_inline size_t block_index(); - simdjson_inline bool has_full_block() const; - simdjson_inline const uint8_t* full_block() const; - /** - * Get the last block, padded with spaces. - * - * There will always be a last block, with at least 1 byte, unless len == 0 (in which case this - * function fills the buffer with spaces and returns 0. In particular, if len == STEP_SIZE there - * will be 0 full_blocks and 1 remainder block with STEP_SIZE bytes and no spaces for padding. - * - * @return the number of effective characters in the last block. - */ - simdjson_inline size_t get_remainder(uint8_t* dst) const; - simdjson_inline void advance(); - private: - const uint8_t* buf; - const size_t len; - const size_t lenminusstep; - size_t idx; - }; +// Walks through a buffer in block-sized increments, loading the last part with spaces +template +struct buf_block_reader { +public: + simdjson_inline buf_block_reader(const uint8_t *_buf, size_t _len); + simdjson_inline size_t block_index(); + simdjson_inline bool has_full_block() const; + simdjson_inline const uint8_t *full_block() const; + /** + * Get the last block, padded with spaces. + * + * There will always be a last block, with at least 1 byte, unless len == 0 (in which case this + * function fills the buffer with spaces and returns 0. In particular, if len == STEP_SIZE there + * will be 0 full_blocks and 1 remainder block with STEP_SIZE bytes and no spaces for padding. + * + * @return the number of effective characters in the last block. + */ + simdjson_inline size_t get_remainder(uint8_t *dst) const; + simdjson_inline void advance(); +private: + const uint8_t *buf; + const size_t len; + const size_t lenminusstep; + size_t idx; +}; - // Routines to print masks and text for debugging bitmask operations - simdjson_unused static char* format_input_text_64(const uint8_t* text) { - static char buf[sizeof(simd8x64) + 1]; - for (size_t i = 0; i < sizeof(simd8x64); i++) { - buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]); - } - buf[sizeof(simd8x64)] = '\0'; - return buf; - } +// Routines to print masks and text for debugging bitmask operations +simdjson_unused static char * format_input_text_64(const uint8_t *text) { + static char buf[sizeof(simd8x64) + 1]; + for (size_t i=0; i); i++) { + buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]); + } + buf[sizeof(simd8x64)] = '\0'; + return buf; +} - // Routines to print masks and text for debugging bitmask operations - simdjson_unused static char* format_input_text(const simd8x64& in) { - static char buf[sizeof(simd8x64) + 1]; - in.store(reinterpret_cast(buf)); - for (size_t i = 0; i < sizeof(simd8x64); i++) { - if (buf[i] < ' ') { buf[i] = '_'; } - } - buf[sizeof(simd8x64)] = '\0'; - return buf; - } +// Routines to print masks and text for debugging bitmask operations +simdjson_unused static char * format_input_text(const simd8x64& in) { + static char buf[sizeof(simd8x64) + 1]; + in.store(reinterpret_cast(buf)); + for (size_t i=0; i); i++) { + if (buf[i] < ' ') { buf[i] = '_'; } + } + buf[sizeof(simd8x64)] = '\0'; + return buf; +} - simdjson_unused static char* format_input_text(const simd8x64& in, uint64_t mask) { - static char buf[sizeof(simd8x64) + 1]; - in.store(reinterpret_cast(buf)); - for (size_t i = 0; i < sizeof(simd8x64); i++) { - if (buf[i] <= ' ') { buf[i] = '_'; } - if (!(mask & (size_t(1) << i))) { buf[i] = ' '; } - } - buf[sizeof(simd8x64)] = '\0'; - return buf; - } +simdjson_unused static char * format_input_text(const simd8x64& in, uint64_t mask) { + static char buf[sizeof(simd8x64) + 1]; + in.store(reinterpret_cast(buf)); + for (size_t i=0; i); i++) { + if (buf[i] <= ' ') { buf[i] = '_'; } + if (!(mask & (size_t(1) << i))) { buf[i] = ' '; } + } + buf[sizeof(simd8x64)] = '\0'; + return buf; +} - simdjson_unused static char* format_mask(uint64_t mask) { - static char buf[sizeof(simd8x64) + 1]; - for (size_t i = 0; i < 64; i++) { - buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' '; - } - buf[64] = '\0'; - return buf; - } +simdjson_unused static char * format_mask(uint64_t mask) { + static char buf[sizeof(simd8x64) + 1]; + for (size_t i=0; i<64; i++) { + buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' '; + } + buf[64] = '\0'; + return buf; +} - template - simdjson_inline buf_block_reader::buf_block_reader(const uint8_t* _buf, size_t _len) : buf{ _buf }, len{ _len }, lenminusstep{ len < STEP_SIZE ? 0 : len - STEP_SIZE }, idx{ 0 } {} +template +simdjson_inline buf_block_reader::buf_block_reader(const uint8_t *_buf, size_t _len) : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE}, idx{0} {} - template - simdjson_inline size_t buf_block_reader::block_index() { return idx; } +template +simdjson_inline size_t buf_block_reader::block_index() { return idx; } - template - simdjson_inline bool buf_block_reader::has_full_block() const { - return idx < lenminusstep; - } +template +simdjson_inline bool buf_block_reader::has_full_block() const { + return idx < lenminusstep; +} - template - simdjson_inline const uint8_t* buf_block_reader::full_block() const { - return &buf[idx]; - } +template +simdjson_inline const uint8_t *buf_block_reader::full_block() const { + return &buf[idx]; +} - template - simdjson_inline size_t buf_block_reader::get_remainder(uint8_t* dst) const { - if (len == idx) { return 0; } // memcpy(dst, null, 0) will trigger an error with some sanitizers - std::memset(dst, 0x20, STEP_SIZE); // std::memset STEP_SIZE because it's more efficient to write out 8 or 16 bytes at once. - std::memcpy(dst, buf + idx, len - idx); - return len - idx; - } +template +simdjson_inline size_t buf_block_reader::get_remainder(uint8_t *dst) const { + if(len == idx) { return 0; } // memcpy(dst, null, 0) will trigger an error with some sanitizers + std::memset(dst, 0x20, STEP_SIZE); // std::memset STEP_SIZE because it's more efficient to write out 8 or 16 bytes at once. + std::memcpy(dst, buf + idx, len - idx); + return len - idx; +} - template - simdjson_inline void buf_block_reader::advance() { - idx += STEP_SIZE; - } +template +simdjson_inline void buf_block_reader::advance() { + idx += STEP_SIZE; +} - } // namespace stage1 - } // unnamed namespace - } // namespace arm64 +} // namespace stage1 +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_BUF_BLOCK_READER_H @@ -11510,145 +12846,145 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { - namespace { - namespace stage1 { +namespace arm64 { +namespace { +namespace stage1 { - /** - * Scans for escape characters in JSON, taking care with multiple backslashes (\\n vs. \n). - */ - struct json_escape_scanner { - /** The actual escape characters (the backslashes themselves). */ - uint64_t next_is_escaped = 0ULL; +/** + * Scans for escape characters in JSON, taking care with multiple backslashes (\\n vs. \n). + */ +struct json_escape_scanner { + /** The actual escape characters (the backslashes themselves). */ + uint64_t next_is_escaped = 0ULL; - struct escaped_and_escape { - /** - * Mask of escaped characters. - * - * ``` - * \n \\n \\\n \\\\n \ - * 0100100010100101000 - * n \ \ n \ \ - * ``` - */ - uint64_t escaped; - /** - * Mask of escape characters. - * - * ``` - * \n \\n \\\n \\\\n \ - * 1001000101001010001 - * \ \ \ \ \ \ \ - * ``` - */ - uint64_t escape; - }; + struct escaped_and_escape { + /** + * Mask of escaped characters. + * + * ``` + * \n \\n \\\n \\\\n \ + * 0100100010100101000 + * n \ \ n \ \ + * ``` + */ + uint64_t escaped; + /** + * Mask of escape characters. + * + * ``` + * \n \\n \\\n \\\\n \ + * 1001000101001010001 + * \ \ \ \ \ \ \ + * ``` + */ + uint64_t escape; + }; - /** - * Get a mask of both escape and escaped characters (the characters following a backslash). - * - * @param potential_escape A mask of the character that can escape others (but could be - * escaped itself). e.g. block.eq('\\') - */ - simdjson_really_inline escaped_and_escape next(uint64_t backslash) noexcept { + /** + * Get a mask of both escape and escaped characters (the characters following a backslash). + * + * @param potential_escape A mask of the character that can escape others (but could be + * escaped itself). e.g. block.eq('\\') + */ + simdjson_really_inline escaped_and_escape next(uint64_t backslash) noexcept { #if !SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT - if (!backslash) { return { next_escaped_without_backslashes(), 0 }; } + if (!backslash) { return {next_escaped_without_backslashes(), 0}; } #endif - // | | Mask (shows characters instead of 1's) | Depth | Instructions | - // |--------------------------------|----------------------------------------|-------|---------------------| - // | string | `\\n_\\\n___\\\n___\\\\___\\\\__\\\` | | | - // | | ` even odd even odd odd` | | | - // | potential_escape | ` \ \\\ \\\ \\\\ \\\\ \\\` | 1 | 1 (backslash & ~first_is_escaped) - // | escape_and_terminal_code | ` \n \ \n \ \n \ \ \ \ \ \` | 5 | 5 (next_escape_and_terminal_code()) - // | escaped | `\ \ n \ n \ \ \ \ \ ` X | 6 | 7 (escape_and_terminal_code ^ (potential_escape | first_is_escaped)) - // | escape | ` \ \ \ \ \ \ \ \ \ \` | 6 | 8 (escape_and_terminal_code & backslash) - // | first_is_escaped | `\ ` | 7 (*) | 9 (escape >> 63) () - // (*) this is not needed until the next iteration - uint64_t escape_and_terminal_code = next_escape_and_terminal_code(backslash & ~this->next_is_escaped); - uint64_t escaped = escape_and_terminal_code ^ (backslash | this->next_is_escaped); - uint64_t escape = escape_and_terminal_code & backslash; - this->next_is_escaped = escape >> 63; - return { escaped, escape }; - } + // | | Mask (shows characters instead of 1's) | Depth | Instructions | + // |--------------------------------|----------------------------------------|-------|---------------------| + // | string | `\\n_\\\n___\\\n___\\\\___\\\\__\\\` | | | + // | | ` even odd even odd odd` | | | + // | potential_escape | ` \ \\\ \\\ \\\\ \\\\ \\\` | 1 | 1 (backslash & ~first_is_escaped) + // | escape_and_terminal_code | ` \n \ \n \ \n \ \ \ \ \ \` | 5 | 5 (next_escape_and_terminal_code()) + // | escaped | `\ \ n \ n \ \ \ \ \ ` X | 6 | 7 (escape_and_terminal_code ^ (potential_escape | first_is_escaped)) + // | escape | ` \ \ \ \ \ \ \ \ \ \` | 6 | 8 (escape_and_terminal_code & backslash) + // | first_is_escaped | `\ ` | 7 (*) | 9 (escape >> 63) () + // (*) this is not needed until the next iteration + uint64_t escape_and_terminal_code = next_escape_and_terminal_code(backslash & ~this->next_is_escaped); + uint64_t escaped = escape_and_terminal_code ^ (backslash | this->next_is_escaped); + uint64_t escape = escape_and_terminal_code & backslash; + this->next_is_escaped = escape >> 63; + return {escaped, escape}; + } - private: - static constexpr const uint64_t ODD_BITS = 0xAAAAAAAAAAAAAAAAULL; +private: + static constexpr const uint64_t ODD_BITS = 0xAAAAAAAAAAAAAAAAULL; - simdjson_really_inline uint64_t next_escaped_without_backslashes() noexcept { - uint64_t escaped = this->next_is_escaped; - this->next_is_escaped = 0; - return escaped; - } + simdjson_really_inline uint64_t next_escaped_without_backslashes() noexcept { + uint64_t escaped = this->next_is_escaped; + this->next_is_escaped = 0; + return escaped; + } - /** - * Returns a mask of the next escape characters (masking out escaped backslashes), along with - * any non-backslash escape codes. - * - * \n \\n \\\n \\\\n returns: - * \n \ \ \n \ \ - * 11 100 1011 10100 - * - * You are expected to mask out the first bit yourself if the previous block had a trailing - * escape. - * - * & the result with potential_escape to get just the escape characters. - * ^ the result with (potential_escape | first_is_escaped) to get escaped characters. - */ - static simdjson_really_inline uint64_t next_escape_and_terminal_code(uint64_t potential_escape) noexcept { - // If we were to just shift and mask out any odd bits, we'd actually get a *half* right answer: - // any even-aligned backslash runs would be correct! Odd-aligned backslash runs would be - // inverted (\\\ would be 010 instead of 101). - // - // ``` - // string: | ____\\\\_\\\\_____ | - // maybe_escaped | ODD | \ \ \ \ | - // even-aligned ^^^ ^^^^ odd-aligned - // ``` - // - // Taking that into account, our basic strategy is: - // - // 1. Use subtraction to produce a mask with 1's for even-aligned runs and 0's for - // odd-aligned runs. - // 2. XOR all odd bits, which masks out the odd bits in even-aligned runs, and brings IN the - // odd bits in odd-aligned runs. - // 3. & with backslash to clean up any stray bits. - // runs are set to 0, and then XORing with "odd": - // - // | | Mask (shows characters instead of 1's) | Instructions | - // |--------------------------------|----------------------------------------|---------------------| - // | string | `\\n_\\\n___\\\n___\\\\___\\\\__\\\` | - // | | ` even odd even odd odd` | - // | maybe_escaped | ` n \\n \\n \\\_ \\\_ \\` X | 1 (potential_escape << 1) - // | maybe_escaped_and_odd | ` \n_ \\n _ \\\n_ _ \\\__ _\\\_ \\\` | 1 (maybe_escaped | odd) - // | even_series_codes_and_odd | ` n_\\\ _ n_ _\\\\ _ _ ` | 1 (maybe_escaped_and_odd - potential_escape) - // | escape_and_terminal_code | ` \n \ \n \ \n \ \ \ \ \ \` | 1 (^ odd) - // + /** + * Returns a mask of the next escape characters (masking out escaped backslashes), along with + * any non-backslash escape codes. + * + * \n \\n \\\n \\\\n returns: + * \n \ \ \n \ \ + * 11 100 1011 10100 + * + * You are expected to mask out the first bit yourself if the previous block had a trailing + * escape. + * + * & the result with potential_escape to get just the escape characters. + * ^ the result with (potential_escape | first_is_escaped) to get escaped characters. + */ + static simdjson_really_inline uint64_t next_escape_and_terminal_code(uint64_t potential_escape) noexcept { + // If we were to just shift and mask out any odd bits, we'd actually get a *half* right answer: + // any even-aligned backslash runs would be correct! Odd-aligned backslash runs would be + // inverted (\\\ would be 010 instead of 101). + // + // ``` + // string: | ____\\\\_\\\\_____ | + // maybe_escaped | ODD | \ \ \ \ | + // even-aligned ^^^ ^^^^ odd-aligned + // ``` + // + // Taking that into account, our basic strategy is: + // + // 1. Use subtraction to produce a mask with 1's for even-aligned runs and 0's for + // odd-aligned runs. + // 2. XOR all odd bits, which masks out the odd bits in even-aligned runs, and brings IN the + // odd bits in odd-aligned runs. + // 3. & with backslash to clean up any stray bits. + // runs are set to 0, and then XORing with "odd": + // + // | | Mask (shows characters instead of 1's) | Instructions | + // |--------------------------------|----------------------------------------|---------------------| + // | string | `\\n_\\\n___\\\n___\\\\___\\\\__\\\` | + // | | ` even odd even odd odd` | + // | maybe_escaped | ` n \\n \\n \\\_ \\\_ \\` X | 1 (potential_escape << 1) + // | maybe_escaped_and_odd | ` \n_ \\n _ \\\n_ _ \\\__ _\\\_ \\\` | 1 (maybe_escaped | odd) + // | even_series_codes_and_odd | ` n_\\\ _ n_ _\\\\ _ _ ` | 1 (maybe_escaped_and_odd - potential_escape) + // | escape_and_terminal_code | ` \n \ \n \ \n \ \ \ \ \ \` | 1 (^ odd) + // - // Escaped characters are characters following an escape. - uint64_t maybe_escaped = potential_escape << 1; + // Escaped characters are characters following an escape. + uint64_t maybe_escaped = potential_escape << 1; - // To distinguish odd from even escape sequences, therefore, we turn on any *starting* - // escapes that are on an odd byte. (We actually bring in all odd bits, for speed.) - // - Odd runs of backslashes are 0000, and the code at the end ("n" in \n or \\n) is 1. - // - Odd runs of backslashes are 1111, and the code at the end ("n" in \n or \\n) is 0. - // - All other odd bytes are 1, and even bytes are 0. - uint64_t maybe_escaped_and_odd_bits = maybe_escaped | ODD_BITS; - uint64_t even_series_codes_and_odd_bits = maybe_escaped_and_odd_bits - potential_escape; + // To distinguish odd from even escape sequences, therefore, we turn on any *starting* + // escapes that are on an odd byte. (We actually bring in all odd bits, for speed.) + // - Odd runs of backslashes are 0000, and the code at the end ("n" in \n or \\n) is 1. + // - Odd runs of backslashes are 1111, and the code at the end ("n" in \n or \\n) is 0. + // - All other odd bytes are 1, and even bytes are 0. + uint64_t maybe_escaped_and_odd_bits = maybe_escaped | ODD_BITS; + uint64_t even_series_codes_and_odd_bits = maybe_escaped_and_odd_bits - potential_escape; - // Now we flip all odd bytes back with xor. This: - // - Makes odd runs of backslashes go from 0000 to 1010 - // - Makes even runs of backslashes go from 1111 to 1010 - // - Sets actually-escaped codes to 1 (the n in \n and \\n: \n = 11, \\n = 100) - // - Resets all other bytes to 0 - return even_series_codes_and_odd_bits ^ ODD_BITS; - } - }; + // Now we flip all odd bytes back with xor. This: + // - Makes odd runs of backslashes go from 0000 to 1010 + // - Makes even runs of backslashes go from 1111 to 1010 + // - Sets actually-escaped codes to 1 (the n in \n and \\n: \n = 11, \\n = 100) + // - Resets all other bytes to 0 + return even_series_codes_and_odd_bits ^ ODD_BITS; + } +}; - } // namespace stage1 - } // unnamed namespace - } // namespace arm64 +} // namespace stage1 +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRING_SCANNER_H @@ -11664,93 +13000,93 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { - namespace { - namespace stage1 { +namespace arm64 { +namespace { +namespace stage1 { - struct json_string_block { - // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 - simdjson_really_inline json_string_block(uint64_t escaped, uint64_t quote, uint64_t in_string) : - _escaped(escaped), _quote(quote), _in_string(in_string) {} +struct json_string_block { + // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 + simdjson_really_inline json_string_block(uint64_t escaped, uint64_t quote, uint64_t in_string) : + _escaped(escaped), _quote(quote), _in_string(in_string) {} - // Escaped characters (characters following an escape() character) - simdjson_really_inline uint64_t escaped() const { return _escaped; } - // Real (non-backslashed) quotes - simdjson_really_inline uint64_t quote() const { return _quote; } - // Only characters inside the string (not including the quotes) - simdjson_really_inline uint64_t string_content() const { return _in_string & ~_quote; } - // Return a mask of whether the given characters are inside a string (only works on non-quotes) - simdjson_really_inline uint64_t non_quote_inside_string(uint64_t mask) const { return mask & _in_string; } - // Return a mask of whether the given characters are inside a string (only works on non-quotes) - simdjson_really_inline uint64_t non_quote_outside_string(uint64_t mask) const { return mask & ~_in_string; } - // Tail of string (everything except the start quote) - simdjson_really_inline uint64_t string_tail() const { return _in_string ^ _quote; } + // Escaped characters (characters following an escape() character) + simdjson_really_inline uint64_t escaped() const { return _escaped; } + // Real (non-backslashed) quotes + simdjson_really_inline uint64_t quote() const { return _quote; } + // Only characters inside the string (not including the quotes) + simdjson_really_inline uint64_t string_content() const { return _in_string & ~_quote; } + // Return a mask of whether the given characters are inside a string (only works on non-quotes) + simdjson_really_inline uint64_t non_quote_inside_string(uint64_t mask) const { return mask & _in_string; } + // Return a mask of whether the given characters are inside a string (only works on non-quotes) + simdjson_really_inline uint64_t non_quote_outside_string(uint64_t mask) const { return mask & ~_in_string; } + // Tail of string (everything except the start quote) + simdjson_really_inline uint64_t string_tail() const { return _in_string ^ _quote; } - // escaped characters (backslashed--does not include the hex characters after \u) - uint64_t _escaped; - // real quotes (non-escaped ones) - uint64_t _quote; - // string characters (includes start quote but not end quote) - uint64_t _in_string; - }; + // escaped characters (backslashed--does not include the hex characters after \u) + uint64_t _escaped; + // real quotes (non-escaped ones) + uint64_t _quote; + // string characters (includes start quote but not end quote) + uint64_t _in_string; +}; - // Scans blocks for string characters, storing the state necessary to do so - class json_string_scanner { - public: - simdjson_really_inline json_string_block next(const simd::simd8x64& in); - // Returns either UNCLOSED_STRING or SUCCESS - simdjson_really_inline error_code finish(); +// Scans blocks for string characters, storing the state necessary to do so +class json_string_scanner { +public: + simdjson_really_inline json_string_block next(const simd::simd8x64& in); + // Returns either UNCLOSED_STRING or SUCCESS + simdjson_really_inline error_code finish(); - private: - // Scans for escape characters - json_escape_scanner escape_scanner{}; - // Whether the last iteration was still inside a string (all 1's = true, all 0's = false). - uint64_t prev_in_string = 0ULL; - }; +private: + // Scans for escape characters + json_escape_scanner escape_scanner{}; + // Whether the last iteration was still inside a string (all 1's = true, all 0's = false). + uint64_t prev_in_string = 0ULL; +}; - // - // Return a mask of all string characters plus end quotes. - // - // prev_escaped is overflow saying whether the next character is escaped. - // prev_in_string is overflow saying whether we're still in a string. - // - // Backslash sequences outside of quotes will be detected in stage 2. - // - simdjson_really_inline json_string_block json_string_scanner::next(const simd::simd8x64& in) { - const uint64_t backslash = in.eq('\\'); - const uint64_t escaped = escape_scanner.next(backslash).escaped; - const uint64_t quote = in.eq('"') & ~escaped; +// +// Return a mask of all string characters plus end quotes. +// +// prev_escaped is overflow saying whether the next character is escaped. +// prev_in_string is overflow saying whether we're still in a string. +// +// Backslash sequences outside of quotes will be detected in stage 2. +// +simdjson_really_inline json_string_block json_string_scanner::next(const simd::simd8x64& in) { + const uint64_t backslash = in.eq('\\'); + const uint64_t escaped = escape_scanner.next(backslash).escaped; + const uint64_t quote = in.eq('"') & ~escaped; - // - // prefix_xor flips on bits inside the string (and flips off the end quote). - // - // Then we xor with prev_in_string: if we were in a string already, its effect is flipped - // (characters inside strings are outside, and characters outside strings are inside). - // - const uint64_t in_string = prefix_xor(quote) ^ prev_in_string; + // + // prefix_xor flips on bits inside the string (and flips off the end quote). + // + // Then we xor with prev_in_string: if we were in a string already, its effect is flipped + // (characters inside strings are outside, and characters outside strings are inside). + // + const uint64_t in_string = prefix_xor(quote) ^ prev_in_string; - // - // Check if we're still in a string at the end of the box so the next block will know - // - prev_in_string = uint64_t(static_cast(in_string) >> 63); + // + // Check if we're still in a string at the end of the box so the next block will know + // + prev_in_string = uint64_t(static_cast(in_string) >> 63); - // Use ^ to turn the beginning quote off, and the end quote on. + // Use ^ to turn the beginning quote off, and the end quote on. - // We are returning a function-local object so either we get a move constructor - // or we get copy elision. - return json_string_block(escaped, quote, in_string); - } + // We are returning a function-local object so either we get a move constructor + // or we get copy elision. + return json_string_block(escaped, quote, in_string); +} - simdjson_really_inline error_code json_string_scanner::finish() { - if (prev_in_string) { - return UNCLOSED_STRING; - } - return SUCCESS; - } +simdjson_really_inline error_code json_string_scanner::finish() { + if (prev_in_string) { + return UNCLOSED_STRING; + } + return SUCCESS; +} - } // namespace stage1 - } // unnamed namespace - } // namespace arm64 +} // namespace stage1 +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRING_SCANNER_H @@ -11766,206 +13102,203 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { - namespace { - namespace utf8_validation { +namespace arm64 { +namespace { +namespace utf8_validation { - using namespace simd; +using namespace simd; - simdjson_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { - // Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) - // Bit 1 = Too Long (ASCII followed by continuation) - // Bit 2 = Overlong 3-byte - // Bit 4 = Surrogate - // Bit 5 = Overlong 2-byte - // Bit 7 = Two Continuations - constexpr const uint8_t TOO_SHORT = 1 << 0; // 11______ 0_______ - // 11______ 11______ - constexpr const uint8_t TOO_LONG = 1 << 1; // 0_______ 10______ - constexpr const uint8_t OVERLONG_3 = 1 << 2; // 11100000 100_____ - constexpr const uint8_t SURROGATE = 1 << 4; // 11101101 101_____ - constexpr const uint8_t OVERLONG_2 = 1 << 5; // 1100000_ 10______ - constexpr const uint8_t TWO_CONTS = 1 << 7; // 10______ 10______ - constexpr const uint8_t TOO_LARGE = 1 << 3; // 11110100 1001____ - // 11110100 101_____ - // 11110101 1001____ - // 11110101 101_____ - // 1111011_ 1001____ - // 1111011_ 101_____ - // 11111___ 1001____ - // 11111___ 101_____ - constexpr const uint8_t TOO_LARGE_1000 = 1 << 6; - // 11110101 1000____ - // 1111011_ 1000____ - // 11111___ 1000____ - constexpr const uint8_t OVERLONG_4 = 1 << 6; // 11110000 1000____ + simdjson_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { +// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) +// Bit 1 = Too Long (ASCII followed by continuation) +// Bit 2 = Overlong 3-byte +// Bit 4 = Surrogate +// Bit 5 = Overlong 2-byte +// Bit 7 = Two Continuations + constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______ + // 11______ 11______ + constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______ + constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____ + constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____ + constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______ + constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______ + constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____ + // 11110100 101_____ + // 11110101 1001____ + // 11110101 101_____ + // 1111011_ 1001____ + // 1111011_ 101_____ + // 11111___ 1001____ + // 11111___ 101_____ + constexpr const uint8_t TOO_LARGE_1000 = 1<<6; + // 11110101 1000____ + // 1111011_ 1000____ + // 11111___ 1000____ + constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____ - const simd8 byte_1_high = prev1.shr<4>().lookup_16( - // 0_______ ________ - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - // 10______ ________ - TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, - // 1100____ ________ - TOO_SHORT | OVERLONG_2, - // 1101____ ________ - TOO_SHORT, - // 1110____ ________ - TOO_SHORT | OVERLONG_3 | SURROGATE, - // 1111____ ________ - TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 - ); - constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . - const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( - // ____0000 ________ - CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, - // ____0001 ________ - CARRY | OVERLONG_2, - // ____001_ ________ - CARRY, - CARRY, + const simd8 byte_1_high = prev1.shr<4>().lookup_16( + // 0_______ ________ + TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, + TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, + // 10______ ________ + TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, + // 1100____ ________ + TOO_SHORT | OVERLONG_2, + // 1101____ ________ + TOO_SHORT, + // 1110____ ________ + TOO_SHORT | OVERLONG_3 | SURROGATE, + // 1111____ ________ + TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 + ); + constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . + const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( + // ____0000 ________ + CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, + // ____0001 ________ + CARRY | OVERLONG_2, + // ____001_ ________ + CARRY, + CARRY, - // ____0100 ________ - CARRY | TOO_LARGE, - // ____0101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____011_ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, + // ____0100 ________ + CARRY | TOO_LARGE, + // ____0101 ________ + CARRY | TOO_LARGE | TOO_LARGE_1000, + // ____011_ ________ + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____1___ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____1101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000 - ); - const simd8 byte_2_high = input.shr<4>().lookup_16( - // ________ 0_______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, + // ____1___ ________ + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + // ____1101 ________ + CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000 + ); + const simd8 byte_2_high = input.shr<4>().lookup_16( + // ________ 0_______ + TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, + TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - // ________ 1000____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, - // ________ 1001____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, - // ________ 101_____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, + // ________ 1000____ + TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, + // ________ 1001____ + TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, + // ________ 101_____ + TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, + TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - // ________ 11______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT - ); - return (byte_1_high & byte_1_low & byte_2_high); - } - simdjson_inline simd8 check_multibyte_lengths(const simd8 input, - const simd8 prev_input, const simd8 sc) { - simd8 prev2 = input.prev<2>(prev_input); - simd8 prev3 = input.prev<3>(prev_input); - simd8 must23 = must_be_2_3_continuation(prev2, prev3); - simd8 must23_80 = must23 & uint8_t(0x80); - return must23_80 ^ sc; - } + // ________ 11______ + TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT + ); + return (byte_1_high & byte_1_low & byte_2_high); + } + simdjson_inline simd8 check_multibyte_lengths(const simd8 input, + const simd8 prev_input, const simd8 sc) { + simd8 prev2 = input.prev<2>(prev_input); + simd8 prev3 = input.prev<3>(prev_input); + simd8 must23 = must_be_2_3_continuation(prev2, prev3); + simd8 must23_80 = must23 & uint8_t(0x80); + return must23_80 ^ sc; + } - // - // Return nonzero if there are incomplete multibyte characters at the end of the block: - // e.g. if there is a 4-byte character, but it's 3 bytes from the end. - // - simdjson_inline simd8 is_incomplete(const simd8 input) { - // If the previous input's last 3 bytes match this, they're too short (they ended at EOF): - // ... 1111____ 111_____ 11______ -#if SIMDJSON_IMPLEMENTATION_ICELAKE - static const uint8_t max_array[64] = { - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0xf0u - 1, 0xe0u - 1, 0xc0u - 1 - }; + // + // Return nonzero if there are incomplete multibyte characters at the end of the block: + // e.g. if there is a 4-byte character, but it's 3 bytes from the end. + // + simdjson_inline simd8 is_incomplete(const simd8 input) { + // If the previous input's last 3 bytes match this, they're too short (they ended at EOF): + // ... 1111____ 111_____ 11______ +#if SIMDJSON_IMPLEMENTATION_ICELAKE || (SIMDJSON_IMPLEMENTATION_RVV_VLS && __riscv_v_fixed_vlen >= 512) + static const uint8_t max_array[64] = { + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0xf0u-1, 0xe0u-1, 0xc0u-1 + }; #else - static const uint8_t max_array[32] = { - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0xf0u - 1, 0xe0u - 1, 0xc0u - 1 - }; + static const uint8_t max_array[32] = { + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0xf0u-1, 0xe0u-1, 0xc0u-1 + }; #endif - const simd8 max_value(&max_array[sizeof(max_array) - sizeof(simd8)]); - return input.gt_bits(max_value); - } + const simd8 max_value(&max_array[sizeof(max_array)-sizeof(simd8)]); + return input.gt_bits(max_value); + } - struct utf8_checker { - // If this is nonzero, there has been a UTF-8 error. - simd8 error; - // The last input we received - simd8 prev_input_block; - // Whether the last input we received was incomplete (used for ASCII fast path) - simd8 prev_incomplete; + struct utf8_checker { + // If this is nonzero, there has been a UTF-8 error. + simd8 error; + // The last input we received + simd8 prev_input_block; + // Whether the last input we received was incomplete (used for ASCII fast path) + simd8 prev_incomplete; - // - // Check whether the current bytes are valid UTF-8. - // - simdjson_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { - // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes - // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) - simd8 prev1 = input.prev<1>(prev_input); - simd8 sc = check_special_cases(input, prev1); - this->error |= check_multibyte_lengths(input, prev_input, sc); - } + // + // Check whether the current bytes are valid UTF-8. + // + simdjson_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { + // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes + // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) + simd8 prev1 = input.prev<1>(prev_input); + simd8 sc = check_special_cases(input, prev1); + this->error |= check_multibyte_lengths(input, prev_input, sc); + } - // The only problem that can happen at EOF is that a multibyte character is too short - // or a byte value too large in the last bytes: check_special_cases only checks for bytes - // too large in the first of two bytes. - simdjson_inline void check_eof() { - // If the previous block had incomplete UTF-8 characters at the end, an ASCII block can't - // possibly finish them. - this->error |= this->prev_incomplete; - } + // The only problem that can happen at EOF is that a multibyte character is too short + // or a byte value too large in the last bytes: check_special_cases only checks for bytes + // too large in the first of two bytes. + simdjson_inline void check_eof() { + // If the previous block had incomplete UTF-8 characters at the end, an ASCII block can't + // possibly finish them. + this->error |= this->prev_incomplete; + } - simdjson_inline void check_next_input(const simd8x64& input) { - if (simdjson_likely(is_ascii(input))) { - this->error |= this->prev_incomplete; - } - else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 1) - || (simd8x64::NUM_CHUNKS == 2) - || (simd8x64::NUM_CHUNKS == 4), - "We support one, two or four chunks per 64-byte block."); - SIMDJSON_IF_CONSTEXPR(simd8x64::NUM_CHUNKS == 1) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - } -else SIMDJSON_IF_CONSTEXPR(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); + simdjson_inline void check_next_input(const simd8x64& input) { + if(simdjson_likely(is_ascii(input))) { + this->error |= this->prev_incomplete; + } else { + // you might think that a for-loop would work, but under Visual Studio, it is not good enough. + static_assert((simd8x64::NUM_CHUNKS == 1) + ||(simd8x64::NUM_CHUNKS == 2) + || (simd8x64::NUM_CHUNKS == 4), + "We support one, two or four chunks per 64-byte block."); + SIMDJSON_IF_CONSTEXPR (simd8x64::NUM_CHUNKS == 1) { + this->check_utf8_bytes(input.get<0>(), this->prev_input_block); + } else SIMDJSON_IF_CONSTEXPR (simd8x64::NUM_CHUNKS == 2) { + this->check_utf8_bytes(input.get<0>(), this->prev_input_block); + this->check_utf8_bytes(input.get<1>(), input.get<0>()); + } else SIMDJSON_IF_CONSTEXPR (simd8x64::NUM_CHUNKS == 4) { + this->check_utf8_bytes(input.get<0>(), this->prev_input_block); + this->check_utf8_bytes(input.get<1>(), input.get<0>()); + this->check_utf8_bytes(input.get<2>(), input.get<1>()); + this->check_utf8_bytes(input.get<3>(), input.get<2>()); } - else SIMDJSON_IF_CONSTEXPR(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - this->prev_incomplete = is_incomplete(input.chunks[simd8x64::NUM_CHUNKS - 1]); - this->prev_input_block = input.chunks[simd8x64::NUM_CHUNKS - 1]; - } - } - // do not forget to call check_eof! - simdjson_inline error_code errors() { - return this->error.any_bits_set_anywhere() ? error_code::UTF8_ERROR : error_code::SUCCESS; - } + this->prev_incomplete = is_incomplete(input.get::NUM_CHUNKS-1>()); + this->prev_input_block = input.get::NUM_CHUNKS-1>(); + } + } + // do not forget to call check_eof! + simdjson_warn_unused simdjson_inline error_code errors() { + return this->error.any_bits_set_anywhere() ? error_code::UTF8_ERROR : error_code::SUCCESS; + } - }; // struct utf8_checker - } // namespace utf8_validation + }; // struct utf8_checker +} // namespace utf8_validation - } // unnamed namespace - } // namespace arm64 +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_UTF8_LOOKUP4_ALGORITHM_H @@ -11982,161 +13315,161 @@ else SIMDJSON_IF_CONSTEXPR(simd8x64::NUM_CHUNKS == 2) { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { - namespace { - namespace stage1 { +namespace arm64 { +namespace { +namespace stage1 { - /** - * A block of scanned json, with information on operators and scalars. - * - * We seek to identify pseudo-structural characters. Anything that is inside - * a string must be omitted (hence & ~_string.string_tail()). - * Otherwise, pseudo-structural characters come in two forms. - * 1. We have the structural characters ([,],{,},:, comma). The - * term 'structural character' is from the JSON RFC. - * 2. We have the 'scalar pseudo-structural characters'. - * Scalars are quotes, and any character except structural characters and white space. - * - * To identify the scalar pseudo-structural characters, we must look at what comes - * before them: it must be a space, a quote or a structural characters. - * Starting with simdjson v0.3, we identify them by - * negation: we identify everything that is followed by a non-quote scalar, - * and we negate that. Whatever remains must be a 'scalar pseudo-structural character'. - */ - struct json_block { - public: - // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 - simdjson_inline json_block(json_string_block&& string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : - _string(std::move(string)), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} - simdjson_inline json_block(json_string_block string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : - _string(string), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} +/** + * A block of scanned json, with information on operators and scalars. + * + * We seek to identify pseudo-structural characters. Anything that is inside + * a string must be omitted (hence & ~_string.string_tail()). + * Otherwise, pseudo-structural characters come in two forms. + * 1. We have the structural characters ([,],{,},:, comma). The + * term 'structural character' is from the JSON RFC. + * 2. We have the 'scalar pseudo-structural characters'. + * Scalars are quotes, and any character except structural characters and white space. + * + * To identify the scalar pseudo-structural characters, we must look at what comes + * before them: it must be a space, a quote or a structural characters. + * Starting with simdjson v0.3, we identify them by + * negation: we identify everything that is followed by a non-quote scalar, + * and we negate that. Whatever remains must be a 'scalar pseudo-structural character'. + */ +struct json_block { +public: + // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 + simdjson_inline json_block(json_string_block&& string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : + _string(std::move(string)), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} + simdjson_inline json_block(json_string_block string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : + _string(string), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} - /** - * The start of structurals. - * In simdjson prior to v0.3, these were called the pseudo-structural characters. - **/ - simdjson_inline uint64_t structural_start() const noexcept { return potential_structural_start() & ~_string.string_tail(); } - /** All JSON whitespace (i.e. not in a string) */ - simdjson_inline uint64_t whitespace() const noexcept { return non_quote_outside_string(_characters.whitespace()); } + /** + * The start of structurals. + * In simdjson prior to v0.3, these were called the pseudo-structural characters. + **/ + simdjson_inline uint64_t structural_start() const noexcept { return potential_structural_start() & ~_string.string_tail(); } + /** All JSON whitespace (i.e. not in a string) */ + simdjson_inline uint64_t whitespace() const noexcept { return non_quote_outside_string(_characters.whitespace()); } - // Helpers + // Helpers - /** Whether the given characters are inside a string (only works on non-quotes) */ - simdjson_inline uint64_t non_quote_inside_string(uint64_t mask) const noexcept { return _string.non_quote_inside_string(mask); } - /** Whether the given characters are outside a string (only works on non-quotes) */ - simdjson_inline uint64_t non_quote_outside_string(uint64_t mask) const noexcept { return _string.non_quote_outside_string(mask); } + /** Whether the given characters are inside a string (only works on non-quotes) */ + simdjson_inline uint64_t non_quote_inside_string(uint64_t mask) const noexcept { return _string.non_quote_inside_string(mask); } + /** Whether the given characters are outside a string (only works on non-quotes) */ + simdjson_inline uint64_t non_quote_outside_string(uint64_t mask) const noexcept { return _string.non_quote_outside_string(mask); } - // string and escape characters - json_string_block _string; - // whitespace, structural characters ('operators'), scalars - json_character_block _characters; - // whether the previous character was a scalar - uint64_t _follows_potential_nonquote_scalar; - private: - // Potential structurals (i.e. disregarding strings) + // string and escape characters + json_string_block _string; + // whitespace, structural characters ('operators'), scalars + json_character_block _characters; + // whether the previous character was a scalar + uint64_t _follows_potential_nonquote_scalar; +private: + // Potential structurals (i.e. disregarding strings) - /** - * structural elements ([,],{,},:, comma) plus scalar starts like 123, true and "abc". - * They may reside inside a string. - **/ - simdjson_inline uint64_t potential_structural_start() const noexcept { return _characters.op() | potential_scalar_start(); } - /** - * The start of non-operator runs, like 123, true and "abc". - * It main reside inside a string. - **/ - simdjson_inline uint64_t potential_scalar_start() const noexcept { - // The term "scalar" refers to anything except structural characters and white space - // (so letters, numbers, quotes). - // Whenever it is preceded by something that is not a structural element ({,},[,],:, ") nor a white-space - // then we know that it is irrelevant structurally. - return _characters.scalar() & ~follows_potential_scalar(); - } - /** - * Whether the given character is immediately after a non-operator like 123, true. - * The characters following a quote are not included. - */ - simdjson_inline uint64_t follows_potential_scalar() const noexcept { - // _follows_potential_nonquote_scalar: is defined as marking any character that follows a character - // that is not a structural element ({,},[,],:, comma) nor a quote (") and that is not a - // white space. - // It is understood that within quoted region, anything at all could be marked (irrelevant). - return _follows_potential_nonquote_scalar; - } - }; + /** + * structural elements ([,],{,},:, comma) plus scalar starts like 123, true and "abc". + * They may reside inside a string. + **/ + simdjson_inline uint64_t potential_structural_start() const noexcept { return _characters.op() | potential_scalar_start(); } + /** + * The start of non-operator runs, like 123, true and "abc". + * It main reside inside a string. + **/ + simdjson_inline uint64_t potential_scalar_start() const noexcept { + // The term "scalar" refers to anything except structural characters and white space + // (so letters, numbers, quotes). + // Whenever it is preceded by something that is not a structural element ({,},[,],:, ") nor a white-space + // then we know that it is irrelevant structurally. + return _characters.scalar() & ~follows_potential_scalar(); + } + /** + * Whether the given character is immediately after a non-operator like 123, true. + * The characters following a quote are not included. + */ + simdjson_inline uint64_t follows_potential_scalar() const noexcept { + // _follows_potential_nonquote_scalar: is defined as marking any character that follows a character + // that is not a structural element ({,},[,],:, comma) nor a quote (") and that is not a + // white space. + // It is understood that within quoted region, anything at all could be marked (irrelevant). + return _follows_potential_nonquote_scalar; + } +}; - /** - * Scans JSON for important bits: structural characters or 'operators', strings, and scalars. - * - * The scanner starts by calculating two distinct things: - * - string characters (taking \" into account) - * - structural characters or 'operators' ([]{},:, comma) - * and scalars (runs of non-operators like 123, true and "abc") - * - * To minimize data dependency (a key component of the scanner's speed), it finds these in parallel: - * in particular, the operator/scalar bit will find plenty of things that are actually part of - * strings. When we're done, json_block will fuse the two together by masking out tokens that are - * part of a string. - */ - class json_scanner { - public: - json_scanner() = default; - simdjson_inline json_block next(const simd::simd8x64& in); - // Returns either UNCLOSED_STRING or SUCCESS - simdjson_inline error_code finish(); +/** + * Scans JSON for important bits: structural characters or 'operators', strings, and scalars. + * + * The scanner starts by calculating two distinct things: + * - string characters (taking \" into account) + * - structural characters or 'operators' ([]{},:, comma) + * and scalars (runs of non-operators like 123, true and "abc") + * + * To minimize data dependency (a key component of the scanner's speed), it finds these in parallel: + * in particular, the operator/scalar bit will find plenty of things that are actually part of + * strings. When we're done, json_block will fuse the two together by masking out tokens that are + * part of a string. + */ +class json_scanner { +public: + json_scanner() = default; + simdjson_inline json_block next(const simd::simd8x64& in); + // Returns either UNCLOSED_STRING or SUCCESS + simdjson_warn_unused simdjson_inline error_code finish(); - private: - // Whether the last character of the previous iteration is part of a scalar token - // (anything except whitespace or a structural character/'operator'). - uint64_t prev_scalar = 0ULL; - json_string_scanner string_scanner{}; - }; +private: + // Whether the last character of the previous iteration is part of a scalar token + // (anything except whitespace or a structural character/'operator'). + uint64_t prev_scalar = 0ULL; + json_string_scanner string_scanner{}; +}; - // - // Check if the current character immediately follows a matching character. - // - // For example, this checks for quotes with backslashes in front of them: - // - // const uint64_t backslashed_quote = in.eq('"') & immediately_follows(in.eq('\'), prev_backslash); - // - simdjson_inline uint64_t follows(const uint64_t match, uint64_t& overflow) { - const uint64_t result = match << 1 | overflow; - overflow = match >> 63; - return result; - } +// +// Check if the current character immediately follows a matching character. +// +// For example, this checks for quotes with backslashes in front of them: +// +// const uint64_t backslashed_quote = in.eq('"') & immediately_follows(in.eq('\'), prev_backslash); +// +simdjson_inline uint64_t follows(const uint64_t match, uint64_t &overflow) { + const uint64_t result = match << 1 | overflow; + overflow = match >> 63; + return result; +} - simdjson_inline json_block json_scanner::next(const simd::simd8x64& in) { - json_string_block strings = string_scanner.next(in); - // identifies the white-space and the structural characters - json_character_block characters = json_character_block::classify(in); - // The term "scalar" refers to anything except structural characters and white space - // (so letters, numbers, quotes). - // We want follows_scalar to mark anything that follows a non-quote scalar (so letters and numbers). - // - // A terminal quote should either be followed by a structural character (comma, brace, bracket, colon) - // or nothing. However, we still want ' "a string"true ' to mark the 't' of 'true' as a potential - // pseudo-structural character just like we would if we had ' "a string" true '; otherwise we - // may need to add an extra check when parsing strings. - // - // Performance: there are many ways to skin this cat. - const uint64_t nonquote_scalar = characters.scalar() & ~strings.quote(); - uint64_t follows_nonquote_scalar = follows(nonquote_scalar, prev_scalar); - // We are returning a function-local object so either we get a move constructor - // or we get copy elision. - return json_block( - strings,// strings is a function-local object so either it moves or the copy is elided. - characters, - follows_nonquote_scalar - ); - } +simdjson_inline json_block json_scanner::next(const simd::simd8x64& in) { + json_string_block strings = string_scanner.next(in); + // identifies the white-space and the structural characters + json_character_block characters = json_character_block::classify(in); + // The term "scalar" refers to anything except structural characters and white space + // (so letters, numbers, quotes). + // We want follows_scalar to mark anything that follows a non-quote scalar (so letters and numbers). + // + // A terminal quote should either be followed by a structural character (comma, brace, bracket, colon) + // or nothing. However, we still want ' "a string"true ' to mark the 't' of 'true' as a potential + // pseudo-structural character just like we would if we had ' "a string" true '; otherwise we + // may need to add an extra check when parsing strings. + // + // Performance: there are many ways to skin this cat. + const uint64_t nonquote_scalar = characters.scalar() & ~strings.quote(); + uint64_t follows_nonquote_scalar = follows(nonquote_scalar, prev_scalar); + // We are returning a function-local object so either we get a move constructor + // or we get copy elision. + return json_block( + strings,// strings is a function-local object so either it moves or the copy is elided. + characters, + follows_nonquote_scalar + ); +} - simdjson_inline error_code json_scanner::finish() { - return string_scanner.finish(); - } +simdjson_warn_unused simdjson_inline error_code json_scanner::finish() { + return string_scanner.finish(); +} - } // namespace stage1 - } // unnamed namespace - } // namespace arm64 +} // namespace stage1 +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_SCANNER_H @@ -12154,99 +13487,99 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { - namespace { - namespace stage1 { +namespace arm64 { +namespace { +namespace stage1 { - /** - * This algorithm is used to quickly identify the last structural position that - * makes up a complete document. - * - * It does this by going backwards and finding the last *document boundary* (a - * place where one value follows another without a comma between them). If the - * last document (the characters after the boundary) has an equal number of - * start and end brackets, it is considered complete. - * - * Simply put, we iterate over the structural characters, starting from - * the end. We consider that we found the end of a JSON document when the - * first element of the pair is NOT one of these characters: '{' '[' ':' ',' - * and when the second element is NOT one of these characters: '}' ']' ':' ','. - * - * This simple comparison works most of the time, but it does not cover cases - * where the batch's structural indexes contain a perfect amount of documents. - * In such a case, we do not have access to the structural index which follows - * the last document, therefore, we do not have access to the second element in - * the pair, and that means we cannot identify the last document. To fix this - * issue, we keep a count of the open and closed curly/square braces we found - * while searching for the pair. When we find a pair AND the count of open and - * closed curly/square braces is the same, we know that we just passed a - * complete document, therefore the last json buffer location is the end of the - * batch. - */ - simdjson_inline uint32_t find_next_document_index(dom_parser_implementation& parser) { - // Variant: do not count separately, just figure out depth - if (parser.n_structural_indexes == 0) { return 0; } - auto arr_cnt = 0; - auto obj_cnt = 0; - for (auto i = parser.n_structural_indexes - 1; i > 0; i--) { - auto idxb = parser.structural_indexes[i]; - switch (parser.buf[idxb]) { - case ':': - case ',': - continue; - case '}': - obj_cnt--; - continue; - case ']': - arr_cnt--; - continue; - case '{': - obj_cnt++; - break; - case '[': - arr_cnt++; - break; - } - auto idxa = parser.structural_indexes[i - 1]; - switch (parser.buf[idxa]) { - case '{': - case '[': - case ':': - case ',': - continue; - } - // Last document is complete, so the next document will appear after! - if (!arr_cnt && !obj_cnt) { - return parser.n_structural_indexes; - } - // Last document is incomplete; mark the document at i + 1 as the next one - return i; - } - // If we made it to the end, we want to finish counting to see if we have a full document. - switch (parser.buf[parser.structural_indexes[0]]) { - case '}': - obj_cnt--; - break; - case ']': - arr_cnt--; - break; - case '{': - obj_cnt++; - break; - case '[': - arr_cnt++; - break; - } - if (!arr_cnt && !obj_cnt) { - // We have a complete document. - return parser.n_structural_indexes; - } - return 0; - } +/** + * This algorithm is used to quickly identify the last structural position that + * makes up a complete document. + * + * It does this by going backwards and finding the last *document boundary* (a + * place where one value follows another without a comma between them). If the + * last document (the characters after the boundary) has an equal number of + * start and end brackets, it is considered complete. + * + * Simply put, we iterate over the structural characters, starting from + * the end. We consider that we found the end of a JSON document when the + * first element of the pair is NOT one of these characters: '{' '[' ':' ',' + * and when the second element is NOT one of these characters: '}' ']' ':' ','. + * + * This simple comparison works most of the time, but it does not cover cases + * where the batch's structural indexes contain a perfect amount of documents. + * In such a case, we do not have access to the structural index which follows + * the last document, therefore, we do not have access to the second element in + * the pair, and that means we cannot identify the last document. To fix this + * issue, we keep a count of the open and closed curly/square braces we found + * while searching for the pair. When we find a pair AND the count of open and + * closed curly/square braces is the same, we know that we just passed a + * complete document, therefore the last json buffer location is the end of the + * batch. + */ +simdjson_inline uint32_t find_next_document_index(dom_parser_implementation &parser) { + // Variant: do not count separately, just figure out depth + if(parser.n_structural_indexes == 0) { return 0; } + auto arr_cnt = 0; + auto obj_cnt = 0; + for (auto i = parser.n_structural_indexes - 1; i > 0; i--) { + auto idxb = parser.structural_indexes[i]; + switch (parser.buf[idxb]) { + case ':': + case ',': + continue; + case '}': + obj_cnt--; + continue; + case ']': + arr_cnt--; + continue; + case '{': + obj_cnt++; + break; + case '[': + arr_cnt++; + break; + } + auto idxa = parser.structural_indexes[i - 1]; + switch (parser.buf[idxa]) { + case '{': + case '[': + case ':': + case ',': + continue; + } + // Last document is complete, so the next document will appear after! + if (!arr_cnt && !obj_cnt) { + return parser.n_structural_indexes; + } + // Last document is incomplete; mark the document at i + 1 as the next one + return i; + } + // If we made it to the end, we want to finish counting to see if we have a full document. + switch (parser.buf[parser.structural_indexes[0]]) { + case '}': + obj_cnt--; + break; + case ']': + arr_cnt--; + break; + case '{': + obj_cnt++; + break; + case '[': + arr_cnt++; + break; + } + if (!arr_cnt && !obj_cnt) { + // We have a complete document. + return parser.n_structural_indexes; + } + return 0; +} - } // namespace stage1 - } // unnamed namespace - } // namespace arm64 +} // namespace stage1 +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_FIND_NEXT_DOCUMENT_INDEX_H @@ -12268,92 +13601,92 @@ namespace simdjson { // "simdjson/stage1.h" (this simplifies amalgation) namespace simdjson { - namespace arm64 { - namespace { - namespace stage1 { +namespace arm64 { +namespace { +namespace stage1 { - class json_minifier { - public: - template - static error_code minify(const uint8_t* buf, size_t len, uint8_t* dst, size_t& dst_len) noexcept; +class json_minifier { +public: + template + static error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) noexcept; - private: - simdjson_inline json_minifier(uint8_t* _dst) - : dst{ _dst } - {} - template - simdjson_inline void step(const uint8_t* block_buf, buf_block_reader& reader) noexcept; - simdjson_inline void next(const simd::simd8x64& in, const json_block& block); - simdjson_inline error_code finish(uint8_t* dst_start, size_t& dst_len); - json_scanner scanner{}; - uint8_t* dst; - }; +private: + simdjson_inline json_minifier(uint8_t *_dst) + : dst{_dst} + {} + template + simdjson_inline void step(const uint8_t *block_buf, buf_block_reader &reader) noexcept; + simdjson_inline void next(const simd::simd8x64& in, const json_block& block); + simdjson_warn_unused simdjson_inline error_code finish(uint8_t *dst_start, size_t &dst_len); + json_scanner scanner{}; + uint8_t *dst; +}; - simdjson_inline void json_minifier::next(const simd::simd8x64& in, const json_block& block) { - uint64_t mask = block.whitespace(); - dst += in.compress(mask, dst); - } +simdjson_inline void json_minifier::next(const simd::simd8x64& in, const json_block& block) { + uint64_t mask = block.whitespace(); + dst += in.compress(mask, dst); +} - simdjson_inline error_code json_minifier::finish(uint8_t* dst_start, size_t& dst_len) { - error_code error = scanner.finish(); - if (error) { dst_len = 0; return error; } - dst_len = dst - dst_start; - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code json_minifier::finish(uint8_t *dst_start, size_t &dst_len) { + error_code error = scanner.finish(); + if (error) { dst_len = 0; return error; } + dst_len = dst - dst_start; + return SUCCESS; +} - template<> - simdjson_inline void json_minifier::step<128>(const uint8_t* block_buf, buf_block_reader<128>& reader) noexcept { - simd::simd8x64 in_1(block_buf); - simd::simd8x64 in_2(block_buf + 64); - json_block block_1 = scanner.next(in_1); - json_block block_2 = scanner.next(in_2); - this->next(in_1, block_1); - this->next(in_2, block_2); - reader.advance(); - } +template<> +simdjson_inline void json_minifier::step<128>(const uint8_t *block_buf, buf_block_reader<128> &reader) noexcept { + simd::simd8x64 in_1(block_buf); + simd::simd8x64 in_2(block_buf+64); + json_block block_1 = scanner.next(in_1); + json_block block_2 = scanner.next(in_2); + this->next(in_1, block_1); + this->next(in_2, block_2); + reader.advance(); +} - template<> - simdjson_inline void json_minifier::step<64>(const uint8_t* block_buf, buf_block_reader<64>& reader) noexcept { - simd::simd8x64 in_1(block_buf); - json_block block_1 = scanner.next(in_1); - this->next(block_buf, block_1); - reader.advance(); - } +template<> +simdjson_inline void json_minifier::step<64>(const uint8_t *block_buf, buf_block_reader<64> &reader) noexcept { + simd::simd8x64 in_1(block_buf); + json_block block_1 = scanner.next(in_1); + this->next(block_buf, block_1); + reader.advance(); +} - template - error_code json_minifier::minify(const uint8_t* buf, size_t len, uint8_t* dst, size_t& dst_len) noexcept { - buf_block_reader reader(buf, len); - json_minifier minifier(dst); +template +error_code json_minifier::minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) noexcept { + buf_block_reader reader(buf, len); + json_minifier minifier(dst); - // Index the first n-1 blocks - while (reader.has_full_block()) { - minifier.step(reader.full_block(), reader); - } + // Index the first n-1 blocks + while (reader.has_full_block()) { + minifier.step(reader.full_block(), reader); + } - // Index the last (remainder) block, padded with spaces - uint8_t block[STEP_SIZE]; - size_t remaining_bytes = reader.get_remainder(block); - if (remaining_bytes > 0) { - // We do not want to write directly to the output stream. Rather, we write - // to a local buffer (for safety). - uint8_t out_block[STEP_SIZE]; - uint8_t* const guarded_dst{ minifier.dst }; - minifier.dst = out_block; - minifier.step(block, reader); - size_t to_write = minifier.dst - out_block; - // In some cases, we could be enticed to consider the padded spaces - // as part of the string. This is fine as long as we do not write more - // than we consumed. - if (to_write > remaining_bytes) { to_write = remaining_bytes; } - memcpy(guarded_dst, out_block, to_write); - minifier.dst = guarded_dst + to_write; - } - return minifier.finish(dst, dst_len); - } + // Index the last (remainder) block, padded with spaces + uint8_t block[STEP_SIZE]; + size_t remaining_bytes = reader.get_remainder(block); + if (remaining_bytes > 0) { + // We do not want to write directly to the output stream. Rather, we write + // to a local buffer (for safety). + uint8_t out_block[STEP_SIZE]; + uint8_t * const guarded_dst{minifier.dst}; + minifier.dst = out_block; + minifier.step(block, reader); + size_t to_write = minifier.dst - out_block; + // In some cases, we could be enticed to consider the padded spaces + // as part of the string. This is fine as long as we do not write more + // than we consumed. + if(to_write > remaining_bytes) { to_write = remaining_bytes; } + memcpy(guarded_dst, out_block, to_write); + minifier.dst = guarded_dst + to_write; + } + return minifier.finish(dst, dst_len); +} - } // namespace stage1 - } // unnamed namespace - } // namespace arm64 +} // namespace stage1 +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_MINIFIER_H @@ -12379,341 +13712,339 @@ namespace simdjson { // "simdjson/stage1.h" (this simplifies amalgation) namespace simdjson { - namespace arm64 { - namespace { - namespace stage1 { +namespace arm64 { +namespace { +namespace stage1 { - class bit_indexer { - public: - uint32_t* tail; +class bit_indexer { +public: + uint32_t *tail; - simdjson_inline bit_indexer(uint32_t* index_buf) : tail(index_buf) {} + simdjson_inline bit_indexer(uint32_t *index_buf) : tail(index_buf) {} #if SIMDJSON_PREFER_REVERSE_BITS - /** - * ARM lacks a fast trailing zero instruction, but it has a fast - * bit reversal instruction and a fast leading zero instruction. - * Thus it may be profitable to reverse the bits (once) and then - * to rely on a sequence of instructions that call the leading - * zero instruction. - * - * Performance notes: - * The chosen routine is not optimal in terms of data dependency - * since zero_leading_bit might require two instructions. However, - * it tends to minimize the total number of instructions which is - * beneficial. - */ - simdjson_inline void write_index(uint32_t idx, uint64_t& rev_bits, int i) { - int lz = leading_zeroes(rev_bits); - this->tail[i] = static_cast(idx) + lz; - rev_bits = zero_leading_bit(rev_bits, lz); - } + /** + * ARM lacks a fast trailing zero instruction, but it has a fast + * bit reversal instruction and a fast leading zero instruction. + * Thus it may be profitable to reverse the bits (once) and then + * to rely on a sequence of instructions that call the leading + * zero instruction. + * + * Performance notes: + * The chosen routine is not optimal in terms of data dependency + * since zero_leading_bit might require two instructions. However, + * it tends to minimize the total number of instructions which is + * beneficial. + */ + simdjson_inline void write_index(uint32_t idx, uint64_t& rev_bits, int i) { + int lz = leading_zeroes(rev_bits); + this->tail[i] = static_cast(idx) + lz; + rev_bits = zero_leading_bit(rev_bits, lz); + } #else - /** - * Under recent x64 systems, we often have both a fast trailing zero - * instruction and a fast 'clear-lower-bit' instruction so the following - * algorithm can be competitive. - */ + /** + * Under recent x64 systems, we often have both a fast trailing zero + * instruction and a fast 'clear-lower-bit' instruction so the following + * algorithm can be competitive. + */ - simdjson_inline void write_index(uint32_t idx, uint64_t& bits, int i) { - this->tail[i] = idx + trailing_zeroes(bits); - bits = clear_lowest_bit(bits); - } + simdjson_inline void write_index(uint32_t idx, uint64_t& bits, int i) { + this->tail[i] = idx + trailing_zeroes(bits); + bits = clear_lowest_bit(bits); + } #endif // SIMDJSON_PREFER_REVERSE_BITS - template - simdjson_inline int write_indexes(uint32_t idx, uint64_t& bits) { - write_index(idx, bits, START); - SIMDJSON_IF_CONSTEXPR(N > 1) { - write_indexes<(N - 1 > 0 ? START + 1 : START), (N - 1 >= 0 ? N - 1 : 1)>(idx, bits); - } - return START + N; - } + template + simdjson_inline int write_indexes(uint32_t idx, uint64_t& bits) { + write_index(idx, bits, START); + SIMDJSON_IF_CONSTEXPR (N > 1) { + write_indexes<(N-1>0?START+1:START), (N-1>=0?N-1:1)>(idx, bits); + } + return START+N; + } - template - simdjson_inline int write_indexes_stepped(uint32_t idx, uint64_t& bits, int cnt) { - write_indexes(idx, bits); - SIMDJSON_IF_CONSTEXPR((START + STEP) < END) { - if (simdjson_unlikely((START + STEP) < cnt)) { - write_indexes_stepped<(START + STEP < END ? START + STEP : END), END, STEP>(idx, bits, cnt); - } - } - return ((END - START) % STEP) == 0 ? END : (END - START) - ((END - START) % STEP) + STEP; - } + template + simdjson_inline int write_indexes_stepped(uint32_t idx, uint64_t& bits, int cnt) { + write_indexes(idx, bits); + SIMDJSON_IF_CONSTEXPR ((START+STEP) < END) { + if (simdjson_unlikely((START+STEP) < cnt)) { + write_indexes_stepped<(START+STEP(idx, bits, cnt); + } + } + return ((END-START) % STEP) == 0 ? END : (END-START) - ((END-START) % STEP) + STEP; + } - // flatten out values in 'bits' assuming that they are are to have values of idx - // plus their position in the bitvector, and store these indexes at - // base_ptr[base] incrementing base as we go - // will potentially store extra values beyond end of valid bits, so base_ptr - // needs to be large enough to handle this - // - // If the kernel sets SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER, then it - // will provide its own version of the code. + // flatten out values in 'bits' assuming that they are are to have values of idx + // plus their position in the bitvector, and store these indexes at + // base_ptr[base] incrementing base as we go + // will potentially store extra values beyond end of valid bits, so base_ptr + // needs to be large enough to handle this + // + // If the kernel sets SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER, then it + // will provide its own version of the code. #ifdef SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER - simdjson_inline void write(uint32_t idx, uint64_t bits); + simdjson_inline void write(uint32_t idx, uint64_t bits); #else - simdjson_inline void write(uint32_t idx, uint64_t bits) { - // In some instances, the next branch is expensive because it is mispredicted. - // Unfortunately, in other cases, - // it helps tremendously. - if (bits == 0) - return; + simdjson_inline void write(uint32_t idx, uint64_t bits) { + // In some instances, the next branch is expensive because it is mispredicted. + // Unfortunately, in other cases, + // it helps tremendously. + if (bits == 0) + return; - int cnt = static_cast(count_ones(bits)); + int cnt = static_cast(count_ones(bits)); #if SIMDJSON_PREFER_REVERSE_BITS - bits = reverse_bits(bits); + bits = reverse_bits(bits); #endif #ifdef SIMDJSON_STRUCTURAL_INDEXER_STEP - static constexpr const int STEP = SIMDJSON_STRUCTURAL_INDEXER_STEP; + static constexpr const int STEP = SIMDJSON_STRUCTURAL_INDEXER_STEP; #else - static constexpr const int STEP = 4; + static constexpr const int STEP = 4; #endif - static constexpr const int STEP_UNTIL = 24; + static constexpr const int STEP_UNTIL = 24; - write_indexes_stepped<0, STEP_UNTIL, STEP>(idx, bits, cnt); - SIMDJSON_IF_CONSTEXPR(STEP_UNTIL < 64) { - if (simdjson_unlikely(STEP_UNTIL < cnt)) { - for (int i = STEP_UNTIL; i < cnt; i++) { - write_index(idx, bits, i); - } - } - } + write_indexes_stepped<0, STEP_UNTIL, STEP>(idx, bits, cnt); + SIMDJSON_IF_CONSTEXPR (STEP_UNTIL < 64) { + if (simdjson_unlikely(STEP_UNTIL < cnt)) { + for (int i=STEP_UNTIL; itail += cnt; - } + this->tail += cnt; + } #endif // SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER - }; +}; - class json_structural_indexer { - public: - /** - * Find the important bits of JSON in a 128-byte chunk, and add them to structural_indexes. - * - * @param partial Setting the partial parameter to true allows the find_structural_bits to - * tolerate unclosed strings. The caller should still ensure that the input is valid UTF-8. If - * you are processing substrings, you may want to call on a function like trimmed_length_safe_utf8. - */ - template - static error_code index(const uint8_t* buf, size_t len, dom_parser_implementation& parser, stage1_mode partial) noexcept; +class json_structural_indexer { +public: + /** + * Find the important bits of JSON in a 128-byte chunk, and add them to structural_indexes. + * + * @param partial Setting the partial parameter to true allows the find_structural_bits to + * tolerate unclosed strings. The caller should still ensure that the input is valid UTF-8. If + * you are processing substrings, you may want to call on a function like trimmed_length_safe_utf8. + */ + template + static error_code index(const uint8_t *buf, size_t len, dom_parser_implementation &parser, stage1_mode partial) noexcept; - private: - simdjson_inline json_structural_indexer(uint32_t* structural_indexes); - template - simdjson_inline void step(const uint8_t* block, buf_block_reader& reader) noexcept; - simdjson_inline void next(const simd::simd8x64& in, const json_block& block, size_t idx); - simdjson_inline error_code finish(dom_parser_implementation& parser, size_t idx, size_t len, stage1_mode partial); +private: + simdjson_inline json_structural_indexer(uint32_t *structural_indexes); + template + simdjson_inline void step(const uint8_t *block, buf_block_reader &reader) noexcept; + simdjson_inline void next(const simd::simd8x64& in, const json_block& block, size_t idx); + simdjson_warn_unused simdjson_inline error_code finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial); - json_scanner scanner{}; - utf8_checker checker{}; - bit_indexer indexer; - uint64_t prev_structurals = 0; - uint64_t unescaped_chars_error = 0; - }; + json_scanner scanner{}; + utf8_checker checker{}; + bit_indexer indexer; + uint64_t prev_structurals = 0; + uint64_t unescaped_chars_error = 0; +}; - simdjson_inline json_structural_indexer::json_structural_indexer(uint32_t* structural_indexes) : indexer{ structural_indexes } {} +simdjson_inline json_structural_indexer::json_structural_indexer(uint32_t *structural_indexes) : indexer{structural_indexes} {} - // Skip the last character if it is partial - simdjson_inline size_t trim_partial_utf8(const uint8_t* buf, size_t len) { - if (simdjson_unlikely(len < 3)) { - switch (len) { - case 2: - if (buf[len - 1] >= 0xc0) { return len - 1; } // 2-, 3- and 4-byte characters with only 1 byte left - if (buf[len - 2] >= 0xe0) { return len - 2; } // 3- and 4-byte characters with only 2 bytes left - return len; - case 1: - if (buf[len - 1] >= 0xc0) { return len - 1; } // 2-, 3- and 4-byte characters with only 1 byte left - return len; - case 0: - return len; - } - } - if (buf[len - 1] >= 0xc0) { return len - 1; } // 2-, 3- and 4-byte characters with only 1 byte left - if (buf[len - 2] >= 0xe0) { return len - 2; } // 3- and 4-byte characters with only 1 byte left - if (buf[len - 3] >= 0xf0) { return len - 3; } // 4-byte characters with only 3 bytes left - return len; - } +// Skip the last character if it is partial +simdjson_inline size_t trim_partial_utf8(const uint8_t *buf, size_t len) { + if (simdjson_unlikely(len < 3)) { + switch (len) { + case 2: + if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left + if (buf[len-2] >= 0xe0) { return len-2; } // 3- and 4-byte characters with only 2 bytes left + return len; + case 1: + if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left + return len; + case 0: + return len; + } + } + if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left + if (buf[len-2] >= 0xe0) { return len-2; } // 3- and 4-byte characters with only 1 byte left + if (buf[len-3] >= 0xf0) { return len-3; } // 4-byte characters with only 3 bytes left + return len; +} - // - // PERF NOTES: - // We pipe 2 inputs through these stages: - // 1. Load JSON into registers. This takes a long time and is highly parallelizable, so we load - // 2 inputs' worth at once so that by the time step 2 is looking for them input, it's available. - // 2. Scan the JSON for critical data: strings, scalars and operators. This is the critical path. - // The output of step 1 depends entirely on this information. These functions don't quite use - // up enough CPU: the second half of the functions is highly serial, only using 1 execution core - // at a time. The second input's scans has some dependency on the first ones finishing it, but - // they can make a lot of progress before they need that information. - // 3. Step 1 does not use enough capacity, so we run some extra stuff while we're waiting for that - // to finish: utf-8 checks and generating the output from the last iteration. - // - // The reason we run 2 inputs at a time, is steps 2 and 3 are *still* not enough to soak up all - // available capacity with just one input. Running 2 at a time seems to give the CPU a good enough - // workout. - // - template - error_code json_structural_indexer::index(const uint8_t* buf, size_t len, dom_parser_implementation& parser, stage1_mode partial) noexcept { - if (simdjson_unlikely(len > parser.capacity())) { return CAPACITY; } - // We guard the rest of the code so that we can assume that len > 0 throughout. - if (len == 0) { return EMPTY; } - if (is_streaming(partial)) { - len = trim_partial_utf8(buf, len); - // If you end up with an empty window after trimming - // the partial UTF-8 bytes, then chances are good that you - // have an UTF-8 formatting error. - if (len == 0) { return UTF8_ERROR; } - } - buf_block_reader reader(buf, len); - json_structural_indexer indexer(parser.structural_indexes.get()); +// +// PERF NOTES: +// We pipe 2 inputs through these stages: +// 1. Load JSON into registers. This takes a long time and is highly parallelizable, so we load +// 2 inputs' worth at once so that by the time step 2 is looking for them input, it's available. +// 2. Scan the JSON for critical data: strings, scalars and operators. This is the critical path. +// The output of step 1 depends entirely on this information. These functions don't quite use +// up enough CPU: the second half of the functions is highly serial, only using 1 execution core +// at a time. The second input's scans has some dependency on the first ones finishing it, but +// they can make a lot of progress before they need that information. +// 3. Step 1 does not use enough capacity, so we run some extra stuff while we're waiting for that +// to finish: utf-8 checks and generating the output from the last iteration. +// +// The reason we run 2 inputs at a time, is steps 2 and 3 are *still* not enough to soak up all +// available capacity with just one input. Running 2 at a time seems to give the CPU a good enough +// workout. +// +template +error_code json_structural_indexer::index(const uint8_t *buf, size_t len, dom_parser_implementation &parser, stage1_mode partial) noexcept { + if (simdjson_unlikely(len > parser.capacity())) { return CAPACITY; } + // We guard the rest of the code so that we can assume that len > 0 throughout. + if (len == 0) { return EMPTY; } + if (is_streaming(partial)) { + len = trim_partial_utf8(buf, len); + // If you end up with an empty window after trimming + // the partial UTF-8 bytes, then chances are good that you + // have an UTF-8 formatting error. + if(len == 0) { return UTF8_ERROR; } + } + buf_block_reader reader(buf, len); + json_structural_indexer indexer(parser.structural_indexes.get()); - // Read all but the last block - while (reader.has_full_block()) { - indexer.step(reader.full_block(), reader); - } - // Take care of the last block (will always be there unless file is empty which is - // not supposed to happen.) - uint8_t block[STEP_SIZE]; - if (simdjson_unlikely(reader.get_remainder(block) == 0)) { return UNEXPECTED_ERROR; } - indexer.step(block, reader); - return indexer.finish(parser, reader.block_index(), len, partial); - } + // Read all but the last block + while (reader.has_full_block()) { + indexer.step(reader.full_block(), reader); + } + // Take care of the last block (will always be there unless file is empty which is + // not supposed to happen.) + uint8_t block[STEP_SIZE]; + if (simdjson_unlikely(reader.get_remainder(block) == 0)) { return UNEXPECTED_ERROR; } + indexer.step(block, reader); + return indexer.finish(parser, reader.block_index(), len, partial); +} - template<> - simdjson_inline void json_structural_indexer::step<128>(const uint8_t* block, buf_block_reader<128>& reader) noexcept { - simd::simd8x64 in_1(block); - simd::simd8x64 in_2(block + 64); - json_block block_1 = scanner.next(in_1); - json_block block_2 = scanner.next(in_2); - this->next(in_1, block_1, reader.block_index()); - this->next(in_2, block_2, reader.block_index() + 64); - reader.advance(); - } +template<> +simdjson_inline void json_structural_indexer::step<128>(const uint8_t *block, buf_block_reader<128> &reader) noexcept { + simd::simd8x64 in_1(block); + simd::simd8x64 in_2(block+64); + json_block block_1 = scanner.next(in_1); + json_block block_2 = scanner.next(in_2); + this->next(in_1, block_1, reader.block_index()); + this->next(in_2, block_2, reader.block_index()+64); + reader.advance(); +} - template<> - simdjson_inline void json_structural_indexer::step<64>(const uint8_t* block, buf_block_reader<64>& reader) noexcept { - simd::simd8x64 in_1(block); - json_block block_1 = scanner.next(in_1); - this->next(in_1, block_1, reader.block_index()); - reader.advance(); - } +template<> +simdjson_inline void json_structural_indexer::step<64>(const uint8_t *block, buf_block_reader<64> &reader) noexcept { + simd::simd8x64 in_1(block); + json_block block_1 = scanner.next(in_1); + this->next(in_1, block_1, reader.block_index()); + reader.advance(); +} - simdjson_inline void json_structural_indexer::next(const simd::simd8x64& in, const json_block& block, size_t idx) { - uint64_t unescaped = in.lteq(0x1F); +simdjson_inline void json_structural_indexer::next(const simd::simd8x64& in, const json_block& block, size_t idx) { + uint64_t unescaped = in.lteq(0x1F); #if SIMDJSON_UTF8VALIDATION - checker.check_next_input(in); + checker.check_next_input(in); #endif - indexer.write(uint32_t(idx - 64), prev_structurals); // Output *last* iteration's structurals to the parser - prev_structurals = block.structural_start(); - unescaped_chars_error |= block.non_quote_inside_string(unescaped); - } + indexer.write(uint32_t(idx-64), prev_structurals); // Output *last* iteration's structurals to the parser + prev_structurals = block.structural_start(); + unescaped_chars_error |= block.non_quote_inside_string(unescaped); +} - simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementation& parser, size_t idx, size_t len, stage1_mode partial) { - // Write out the final iteration's structurals - indexer.write(uint32_t(idx - 64), prev_structurals); - error_code error = scanner.finish(); - // We deliberately break down the next expression so that it is - // human readable. - const bool should_we_exit = is_streaming(partial) ? - ((error != SUCCESS) && (error != UNCLOSED_STRING)) // when partial we tolerate UNCLOSED_STRING - : (error != SUCCESS); // if partial is false, we must have SUCCESS - const bool have_unclosed_string = (error == UNCLOSED_STRING); - if (simdjson_unlikely(should_we_exit)) { return error; } +simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial) { + // Write out the final iteration's structurals + indexer.write(uint32_t(idx-64), prev_structurals); + error_code error = scanner.finish(); + // We deliberately break down the next expression so that it is + // human readable. + const bool should_we_exit = is_streaming(partial) ? + ((error != SUCCESS) && (error != UNCLOSED_STRING)) // when partial we tolerate UNCLOSED_STRING + : (error != SUCCESS); // if partial is false, we must have SUCCESS + const bool have_unclosed_string = (error == UNCLOSED_STRING); + if (simdjson_unlikely(should_we_exit)) { return error; } - if (unescaped_chars_error) { - return UNESCAPED_CHARS; - } - parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get()); - /*** - * The On-Demand API requires special padding. - * - * This is related to https://github.com/simdjson/simdjson/issues/906 - * Basically, we want to make sure that if the parsing continues beyond the last (valid) - * structural character, it quickly stops. - * Only three structural characters can be repeated without triggering an error in JSON: [,] and }. - * We repeat the padding character (at 'len'). We don't know what it is, but if the parsing - * continues, then it must be [,] or }. - * Suppose it is ] or }. We backtrack to the first character, what could it be that would - * not trigger an error? It could be ] or } but no, because you can't start a document that way. - * It can't be a comma, a colon or any simple value. So the only way we could continue is - * if the repeated character is [. But if so, the document must start with [. But if the document - * starts with [, it should end with ]. If we enforce that rule, then we would get - * ][[ which is invalid. - * - * This is illustrated with the test array_iterate_unclosed_error() on the following input: - * R"({ "a": [,,)" - **/ - parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); // used later in partial == stage1_mode::streaming_final - parser.structural_indexes[parser.n_structural_indexes + 1] = uint32_t(len); - parser.structural_indexes[parser.n_structural_indexes + 2] = 0; - parser.next_structural_index = 0; - // a valid JSON file cannot have zero structural indexes - we should have found something - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { - return EMPTY; - } - if (simdjson_unlikely(parser.structural_indexes[parser.n_structural_indexes - 1] > len)) { - return UNEXPECTED_ERROR; - } - if (partial == stage1_mode::streaming_partial) { - // If we have an unclosed string, then the last structural - // will be the quote and we want to make sure to omit it. - if (have_unclosed_string) { - parser.n_structural_indexes--; - // a valid JSON file cannot have zero structural indexes - we should have found something - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; } - } - // We truncate the input to the end of the last complete document (or zero). - auto new_structural_indexes = find_next_document_index(parser); - if (new_structural_indexes == 0 && parser.n_structural_indexes > 0) { - if (parser.structural_indexes[0] == 0) { - // If the buffer is partial and we started at index 0 but the document is - // incomplete, it's too big to parse. - return CAPACITY; - } - else { - // It is possible that the document could be parsed, we just had a lot - // of white space. - parser.n_structural_indexes = 0; - return EMPTY; - } - } + if (unescaped_chars_error) { + return UNESCAPED_CHARS; + } + parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get()); + /*** + * The On-Demand API requires special padding. + * + * This is related to https://github.com/simdjson/simdjson/issues/906 + * Basically, we want to make sure that if the parsing continues beyond the last (valid) + * structural character, it quickly stops. + * Only three structural characters can be repeated without triggering an error in JSON: [,] and }. + * We repeat the padding character (at 'len'). We don't know what it is, but if the parsing + * continues, then it must be [,] or }. + * Suppose it is ] or }. We backtrack to the first character, what could it be that would + * not trigger an error? It could be ] or } but no, because you can't start a document that way. + * It can't be a comma, a colon or any simple value. So the only way we could continue is + * if the repeated character is [. But if so, the document must start with [. But if the document + * starts with [, it should end with ]. If we enforce that rule, then we would get + * ][[ which is invalid. + * + * This is illustrated with the test array_iterate_unclosed_error() on the following input: + * R"({ "a": [,,)" + **/ + parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); // used later in partial == stage1_mode::streaming_final + parser.structural_indexes[parser.n_structural_indexes + 1] = uint32_t(len); + parser.structural_indexes[parser.n_structural_indexes + 2] = 0; + parser.next_structural_index = 0; + // a valid JSON file cannot have zero structural indexes - we should have found something + if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { + return EMPTY; + } + if (simdjson_unlikely(parser.structural_indexes[parser.n_structural_indexes - 1] > len)) { + return UNEXPECTED_ERROR; + } + if (partial == stage1_mode::streaming_partial) { + // If we have an unclosed string, then the last structural + // will be the quote and we want to make sure to omit it. + if(have_unclosed_string) { + parser.n_structural_indexes--; + // a valid JSON file cannot have zero structural indexes - we should have found something + if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; } + } + // We truncate the input to the end of the last complete document (or zero). + auto new_structural_indexes = find_next_document_index(parser); + if (new_structural_indexes == 0 && parser.n_structural_indexes > 0) { + if(parser.structural_indexes[0] == 0) { + // If the buffer is partial and we started at index 0 but the document is + // incomplete, it's too big to parse. + return CAPACITY; + } else { + // It is possible that the document could be parsed, we just had a lot + // of white space. + parser.n_structural_indexes = 0; + return EMPTY; + } + } - parser.n_structural_indexes = new_structural_indexes; - } - else if (partial == stage1_mode::streaming_final) { - if (have_unclosed_string) { parser.n_structural_indexes--; } - // We truncate the input to the end of the last complete document (or zero). - // Because partial == stage1_mode::streaming_final, it means that we may - // silently ignore trailing garbage. Though it sounds bad, we do it - // deliberately because many people who have streams of JSON documents - // will truncate them for processing. E.g., imagine that you are uncompressing - // the data from a size file or receiving it in chunks from the network. You - // may not know where exactly the last document will be. Meanwhile the - // document_stream instances allow people to know the JSON documents they are - // parsing (see the iterator.source() method). - parser.n_structural_indexes = find_next_document_index(parser); - // We store the initial n_structural_indexes so that the client can see - // whether we used truncation. If initial_n_structural_indexes == parser.n_structural_indexes, - // then this will query parser.structural_indexes[parser.n_structural_indexes] which is len, - // otherwise, it will copy some prior index. - parser.structural_indexes[parser.n_structural_indexes + 1] = parser.structural_indexes[parser.n_structural_indexes]; - // This next line is critical, do not change it unless you understand what you are - // doing. - parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { - // We tolerate an unclosed string at the very end of the stream. Indeed, users - // often load their data in bulk without being careful and they want us to ignore - // the trailing garbage. - return EMPTY; - } - } - checker.check_eof(); - return checker.errors(); - } + parser.n_structural_indexes = new_structural_indexes; + } else if (partial == stage1_mode::streaming_final) { + if(have_unclosed_string) { parser.n_structural_indexes--; } + // We truncate the input to the end of the last complete document (or zero). + // Because partial == stage1_mode::streaming_final, it means that we may + // silently ignore trailing garbage. Though it sounds bad, we do it + // deliberately because many people who have streams of JSON documents + // will truncate them for processing. E.g., imagine that you are uncompressing + // the data from a size file or receiving it in chunks from the network. You + // may not know where exactly the last document will be. Meanwhile the + // document_stream instances allow people to know the JSON documents they are + // parsing (see the iterator.source() method). + parser.n_structural_indexes = find_next_document_index(parser); + // We store the initial n_structural_indexes so that the client can see + // whether we used truncation. If initial_n_structural_indexes == parser.n_structural_indexes, + // then this will query parser.structural_indexes[parser.n_structural_indexes] which is len, + // otherwise, it will copy some prior index. + parser.structural_indexes[parser.n_structural_indexes + 1] = parser.structural_indexes[parser.n_structural_indexes]; + // This next line is critical, do not change it unless you understand what you are + // doing. + parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); + if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { + // We tolerate an unclosed string at the very end of the stream. Indeed, users + // often load their data in bulk without being careful and they want us to ignore + // the trailing garbage. + return EMPTY; + } + } + checker.check_eof(); + return checker.errors(); +} - } // namespace stage1 - } // unnamed namespace - } // namespace arm64 +} // namespace stage1 +} // unnamed namespace +} // namespace arm64 } // namespace simdjson // Clear CUSTOM_BIT_INDEXER so other implementations can set it if they need to. @@ -12733,38 +14064,38 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { - namespace { - namespace stage1 { +namespace arm64 { +namespace { +namespace stage1 { - /** - * Validates that the string is actual UTF-8. - */ - template - bool generic_validate_utf8(const uint8_t* input, size_t length) { - checker c{}; - buf_block_reader<64> reader(input, length); - while (reader.has_full_block()) { - simd::simd8x64 in(reader.full_block()); - c.check_next_input(in); - reader.advance(); - } - uint8_t block[64]{}; - reader.get_remainder(block); - simd::simd8x64 in(block); - c.check_next_input(in); - reader.advance(); - c.check_eof(); - return c.errors() == error_code::SUCCESS; - } +/** + * Validates that the string is actual UTF-8. + */ +template +bool generic_validate_utf8(const uint8_t * input, size_t length) { + checker c{}; + buf_block_reader<64> reader(input, length); + while (reader.has_full_block()) { + simd::simd8x64 in(reader.full_block()); + c.check_next_input(in); + reader.advance(); + } + uint8_t block[64]{}; + reader.get_remainder(block); + simd::simd8x64 in(block); + c.check_next_input(in); + reader.advance(); + c.check_eof(); + return c.errors() == error_code::SUCCESS; +} - bool generic_validate_utf8(const char* input, size_t length) { - return generic_validate_utf8(reinterpret_cast(input), length); - } +bool generic_validate_utf8(const char * input, size_t length) { + return generic_validate_utf8(reinterpret_cast(input),length); +} - } // namespace stage1 - } // unnamed namespace - } // namespace arm64 +} // namespace stage1 +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_UTF8_VALIDATOR_H @@ -12783,18 +14114,18 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { - namespace { - namespace stage2 { +namespace arm64 { +namespace { +namespace stage2 { - class json_iterator; - class structural_iterator; - struct tape_builder; - struct tape_writer; +class json_iterator; +class structural_iterator; +struct tape_builder; +struct tape_writer; - } // namespace stage2 - } // unnamed namespace - } // namespace arm64 +} // namespace stage2 +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_BASE_H @@ -12812,109 +14143,109 @@ namespace simdjson { #include namespace simdjson { - namespace arm64 { - namespace { - namespace stage2 { +namespace arm64 { +namespace { +namespace stage2 { - struct tape_writer { - /** The next place to write to tape */ - uint64_t* next_tape_loc; +struct tape_writer { + /** The next place to write to tape */ + uint64_t *next_tape_loc; - /** Write a signed 64-bit value to tape. */ - simdjson_inline void append_s64(int64_t value) noexcept; + /** Write a signed 64-bit value to tape. */ + simdjson_inline void append_s64(int64_t value) noexcept; - /** Write an unsigned 64-bit value to tape. */ - simdjson_inline void append_u64(uint64_t value) noexcept; + /** Write an unsigned 64-bit value to tape. */ + simdjson_inline void append_u64(uint64_t value) noexcept; - /** Write a double value to tape. */ - simdjson_inline void append_double(double value) noexcept; + /** Write a double value to tape. */ + simdjson_inline void append_double(double value) noexcept; - /** - * Append a tape entry (an 8-bit type,and 56 bits worth of value). - */ - simdjson_inline void append(uint64_t val, internal::tape_type t) noexcept; + /** + * Append a tape entry (an 8-bit type,and 56 bits worth of value). + */ + simdjson_inline void append(uint64_t val, internal::tape_type t) noexcept; - /** - * Skip the current tape entry without writing. - * - * Used to skip the start of the container, since we'll come back later to fill it in when the - * container ends. - */ - simdjson_inline void skip() noexcept; + /** + * Skip the current tape entry without writing. + * + * Used to skip the start of the container, since we'll come back later to fill it in when the + * container ends. + */ + simdjson_inline void skip() noexcept; - /** - * Skip the number of tape entries necessary to write a large u64 or i64. - */ - simdjson_inline void skip_large_integer() noexcept; + /** + * Skip the number of tape entries necessary to write a large u64 or i64. + */ + simdjson_inline void skip_large_integer() noexcept; - /** - * Skip the number of tape entries necessary to write a double. - */ - simdjson_inline void skip_double() noexcept; + /** + * Skip the number of tape entries necessary to write a double. + */ + simdjson_inline void skip_double() noexcept; - /** - * Write a value to a known location on tape. - * - * Used to go back and write out the start of a container after the container ends. - */ - simdjson_inline static void write(uint64_t& tape_loc, uint64_t val, internal::tape_type t) noexcept; + /** + * Write a value to a known location on tape. + * + * Used to go back and write out the start of a container after the container ends. + */ + simdjson_inline static void write(uint64_t &tape_loc, uint64_t val, internal::tape_type t) noexcept; - private: - /** - * Append both the tape entry, and a supplementary value following it. Used for types that need - * all 64 bits, such as double and uint64_t. - */ - template - simdjson_inline void append2(uint64_t val, T val2, internal::tape_type t) noexcept; - }; // struct tape_writer +private: + /** + * Append both the tape entry, and a supplementary value following it. Used for types that need + * all 64 bits, such as double and uint64_t. + */ + template + simdjson_inline void append2(uint64_t val, T val2, internal::tape_type t) noexcept; +}; // struct tape_writer - simdjson_inline void tape_writer::append_s64(int64_t value) noexcept { - append2(0, value, internal::tape_type::INT64); - } +simdjson_inline void tape_writer::append_s64(int64_t value) noexcept { + append2(0, value, internal::tape_type::INT64); +} - simdjson_inline void tape_writer::append_u64(uint64_t value) noexcept { - append(0, internal::tape_type::UINT64); - *next_tape_loc = value; - next_tape_loc++; - } +simdjson_inline void tape_writer::append_u64(uint64_t value) noexcept { + append(0, internal::tape_type::UINT64); + *next_tape_loc = value; + next_tape_loc++; +} - /** Write a double value to tape. */ - simdjson_inline void tape_writer::append_double(double value) noexcept { - append2(0, value, internal::tape_type::DOUBLE); - } +/** Write a double value to tape. */ +simdjson_inline void tape_writer::append_double(double value) noexcept { + append2(0, value, internal::tape_type::DOUBLE); +} - simdjson_inline void tape_writer::skip() noexcept { - next_tape_loc++; - } +simdjson_inline void tape_writer::skip() noexcept { + next_tape_loc++; +} - simdjson_inline void tape_writer::skip_large_integer() noexcept { - next_tape_loc += 2; - } +simdjson_inline void tape_writer::skip_large_integer() noexcept { + next_tape_loc += 2; +} - simdjson_inline void tape_writer::skip_double() noexcept { - next_tape_loc += 2; - } +simdjson_inline void tape_writer::skip_double() noexcept { + next_tape_loc += 2; +} - simdjson_inline void tape_writer::append(uint64_t val, internal::tape_type t) noexcept { - *next_tape_loc = val | ((uint64_t(char(t))) << 56); - next_tape_loc++; - } +simdjson_inline void tape_writer::append(uint64_t val, internal::tape_type t) noexcept { + *next_tape_loc = val | ((uint64_t(char(t))) << 56); + next_tape_loc++; +} - template - simdjson_inline void tape_writer::append2(uint64_t val, T val2, internal::tape_type t) noexcept { - append(val, t); - static_assert(sizeof(val2) == sizeof(*next_tape_loc), "Type is not 64 bits!"); - memcpy(next_tape_loc, &val2, sizeof(val2)); - next_tape_loc++; - } +template +simdjson_inline void tape_writer::append2(uint64_t val, T val2, internal::tape_type t) noexcept { + append(val, t); + static_assert(sizeof(val2) == sizeof(*next_tape_loc), "Type is not 64 bits!"); + memcpy(next_tape_loc, &val2, sizeof(val2)); + next_tape_loc++; +} - simdjson_inline void tape_writer::write(uint64_t& tape_loc, uint64_t val, internal::tape_type t) noexcept { - tape_loc = val | ((uint64_t(char(t))) << 56); - } +simdjson_inline void tape_writer::write(uint64_t &tape_loc, uint64_t val, internal::tape_type t) noexcept { + tape_loc = val | ((uint64_t(char(t))) << 56); +} - } // namespace stage2 - } // unnamed namespace - } // namespace arm64 +} // namespace stage2 +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_WRITER_H @@ -12934,92 +14265,90 @@ namespace simdjson { // This is for an internal-only stage 2 specific logger. // Set LOG_ENABLED = true to log what stage 2 is doing! namespace simdjson { - namespace arm64 { - namespace { - namespace logger { +namespace arm64 { +namespace { +namespace logger { - static constexpr const char* DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"; + static constexpr const char * DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"; #if SIMDJSON_VERBOSE_LOGGING - static constexpr const bool LOG_ENABLED = true; + static constexpr const bool LOG_ENABLED = true; #else - static constexpr const bool LOG_ENABLED = false; + static constexpr const bool LOG_ENABLED = false; #endif - 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 constexpr const int LOG_INDEX_LEN = 5; + 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 constexpr const int LOG_INDEX_LEN = 5; - static int log_depth; // Not threadsafe. Log only. + static int log_depth; // Not threadsafe. Log only. - // Helper to turn unprintable or newline characters into spaces - static simdjson_inline char printable_char(char c) { - if (c >= 0x20) { - return c; - } - else { - return ' '; - } - } + // Helper to turn unprintable or newline characters into spaces + static simdjson_inline char printable_char(char c) { + if (c >= 0x20) { + return c; + } else { + return ' '; + } + } - // Print the header and set up log_start - static simdjson_inline void log_start() { - if (LOG_ENABLED) { - log_depth = 0; - printf("\n"); - printf("| %-*s | %-*s | %-*s | %-*s | Detail |\n", LOG_EVENT_LEN, "Event", LOG_BUFFER_LEN, "Buffer", LOG_SMALL_BUFFER_LEN, "Next", 5, "Next#"); - printf("|%.*s|%.*s|%.*s|%.*s|--------|\n", LOG_EVENT_LEN + 2, DASHES, LOG_BUFFER_LEN + 2, DASHES, LOG_SMALL_BUFFER_LEN + 2, DASHES, 5 + 2, DASHES); - } - } + // Print the header and set up log_start + static simdjson_inline void log_start() { + if (LOG_ENABLED) { + log_depth = 0; + printf("\n"); + printf("| %-*s | %-*s | %-*s | %-*s | Detail |\n", LOG_EVENT_LEN, "Event", LOG_BUFFER_LEN, "Buffer", LOG_SMALL_BUFFER_LEN, "Next", 5, "Next#"); + printf("|%.*s|%.*s|%.*s|%.*s|--------|\n", LOG_EVENT_LEN+2, DASHES, LOG_BUFFER_LEN+2, DASHES, LOG_SMALL_BUFFER_LEN+2, DASHES, 5+2, DASHES); + } + } - simdjson_unused static simdjson_inline void log_string(const char* message) { - if (LOG_ENABLED) { - printf("%s\n", message); - } - } + simdjson_unused static simdjson_inline void log_string(const char *message) { + if (LOG_ENABLED) { + printf("%s\n", message); + } + } - // Logs a single line from the stage 2 DOM parser - template - static simdjson_inline void log_line(S& structurals, const char* title_prefix, const char* title, const char* detail) { - if (LOG_ENABLED) { - printf("| %*s%s%-*s ", log_depth * 2, "", title_prefix, LOG_EVENT_LEN - log_depth * 2 - int(strlen(title_prefix)), title); - auto current_index = structurals.at_beginning() ? nullptr : structurals.next_structural - 1; - auto next_index = structurals.next_structural; - auto current = current_index ? &structurals.buf[*current_index] : reinterpret_cast(" "); - auto next = &structurals.buf[*next_index]; - { - // Print the next N characters in the buffer. - printf("| "); - // Otherwise, print the characters starting from the buffer position. - // Print spaces for unprintable or newline characters. - for (int i = 0; i < LOG_BUFFER_LEN; i++) { - printf("%c", printable_char(current[i])); - } - printf(" "); - // Print the next N characters in the buffer. - printf("| "); - // Otherwise, print the characters starting from the buffer position. - // Print spaces for unprintable or newline characters. - for (int i = 0; i < LOG_SMALL_BUFFER_LEN; i++) { - printf("%c", printable_char(next[i])); - } - printf(" "); - } - if (current_index) { - printf("| %*u ", LOG_INDEX_LEN, *current_index); - } - else { - printf("| %-*s ", LOG_INDEX_LEN, ""); - } - // printf("| %*u ", LOG_INDEX_LEN, structurals.next_tape_index()); - printf("| %-s ", detail); - printf("|\n"); - } - } + // Logs a single line from the stage 2 DOM parser + template + static simdjson_inline void log_line(S &structurals, const char *title_prefix, const char *title, const char *detail) { + if (LOG_ENABLED) { + printf("| %*s%s%-*s ", log_depth*2, "", title_prefix, LOG_EVENT_LEN - log_depth*2 - int(strlen(title_prefix)), title); + auto current_index = structurals.at_beginning() ? nullptr : structurals.next_structural-1; + auto next_index = structurals.next_structural; + auto current = current_index ? &structurals.buf[*current_index] : reinterpret_cast(" "); + auto next = &structurals.buf[*next_index]; + { + // Print the next N characters in the buffer. + printf("| "); + // Otherwise, print the characters starting from the buffer position. + // Print spaces for unprintable or newline characters. + for (int i=0;i - simdjson_warn_unused simdjson_inline error_code walk_document(V& visitor) noexcept; + /** + * Walk the JSON document. + * + * The visitor receives callbacks when values are encountered. All callbacks pass the iterator as + * the first parameter; some callbacks have other parameters as well: + * + * - visit_document_start() - at the beginning. + * - visit_document_end() - at the end (if things were successful). + * + * - visit_array_start() - at the start `[` of a non-empty array. + * - visit_array_end() - at the end `]` of a non-empty array. + * - visit_empty_array() - when an empty array is encountered. + * + * - visit_object_end() - at the start `]` of a non-empty object. + * - visit_object_start() - at the end `]` of a non-empty object. + * - visit_empty_object() - when an empty object is encountered. + * - visit_key(const uint8_t *key) - when a key in an object field is encountered. key is + * guaranteed to point at the first quote of the string (`"key"`). + * - visit_primitive(const uint8_t *value) - when a value is a string, number, boolean or null. + * - visit_root_primitive(iter, uint8_t *value) - when the top-level value is a string, number, boolean or null. + * + * - increment_count(iter) - each time a value is found in an array or object. + */ + template + simdjson_warn_unused simdjson_inline error_code walk_document(V &visitor) noexcept; - /** - * Create an iterator capable of walking a JSON document. - * - * The document must have already passed through stage 1. - */ - simdjson_inline json_iterator(dom_parser_implementation& _dom_parser, size_t start_structural_index); + /** + * Create an iterator capable of walking a JSON document. + * + * The document must have already passed through stage 1. + */ + simdjson_inline json_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index); - /** - * Look at the next token. - * - * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). - * - * They may include invalid JSON as well (such as `1.2.3` or `ture`). - */ - simdjson_inline const uint8_t* peek() const noexcept; - /** - * Advance to the next token. - * - * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). - * - * They may include invalid JSON as well (such as `1.2.3` or `ture`). - */ - simdjson_inline const uint8_t* advance() noexcept; - /** - * Get the remaining length of the document, from the start of the current token. - */ - simdjson_inline size_t remaining_len() const noexcept; - /** - * Check if we are at the end of the document. - * - * If this is true, there are no more tokens. - */ - simdjson_inline bool at_eof() const noexcept; - /** - * Check if we are at the beginning of the document. - */ - simdjson_inline bool at_beginning() const noexcept; - simdjson_inline uint8_t last_structural() const noexcept; + /** + * Look at the next token. + * + * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). + * + * They may include invalid JSON as well (such as `1.2.3` or `ture`). + */ + simdjson_inline const uint8_t *peek() const noexcept; + /** + * Advance to the next token. + * + * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). + * + * They may include invalid JSON as well (such as `1.2.3` or `ture`). + */ + simdjson_inline const uint8_t *advance() noexcept; + /** + * Get the remaining length of the document, from the start of the current token. + */ + simdjson_inline size_t remaining_len() const noexcept; + /** + * Check if we are at the end of the document. + * + * If this is true, there are no more tokens. + */ + simdjson_inline bool at_eof() const noexcept; + /** + * Check if we are at the beginning of the document. + */ + simdjson_inline bool at_beginning() const noexcept; + simdjson_inline uint8_t last_structural() const noexcept; - /** - * Log that a value has been found. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_value(const char* type) const noexcept; - /** - * Log the start of a multipart value. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_start_value(const char* type) const noexcept; - /** - * Log the end of a multipart value. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_end_value(const char* type) const noexcept; - /** - * Log an error. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_error(const char* error) const noexcept; + /** + * Log that a value has been found. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_value(const char *type) const noexcept; + /** + * Log the start of a multipart value. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_start_value(const char *type) const noexcept; + /** + * Log the end of a multipart value. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_end_value(const char *type) const noexcept; + /** + * Log an error. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_error(const char *error) const noexcept; - template - simdjson_warn_unused simdjson_inline error_code visit_root_primitive(V& visitor, const uint8_t* value) noexcept; - template - simdjson_warn_unused simdjson_inline error_code visit_primitive(V& visitor, const uint8_t* value) noexcept; - }; + template + simdjson_warn_unused simdjson_inline error_code visit_root_primitive(V &visitor, const uint8_t *value) noexcept; + template + simdjson_warn_unused simdjson_inline error_code visit_primitive(V &visitor, const uint8_t *value) noexcept; +}; - template - simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V& visitor) noexcept { - logger::log_start(); +template +simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &visitor) noexcept { + logger::log_start(); - // - // Start the document - // - if (at_eof()) { return EMPTY; } - log_start_value("document"); - SIMDJSON_TRY(visitor.visit_document_start(*this)); + // + // Start the document + // + if (at_eof()) { return EMPTY; } + log_start_value("document"); + SIMDJSON_TRY( visitor.visit_document_start(*this) ); - // - // Read first value - // - { - auto value = advance(); + // + // Read first value + // + { + auto value = advance(); - // Make sure the outer object or array is closed before continuing; otherwise, there are ways we - // could get into memory corruption. See https://github.com/simdjson/simdjson/issues/906 - if (!STREAMING) { - switch (*value) { - case '{': if (last_structural() != '}') { log_value("starting brace unmatched"); return TAPE_ERROR; }; break; - case '[': if (last_structural() != ']') { log_value("starting bracket unmatched"); return TAPE_ERROR; }; break; - } - } + // Make sure the outer object or array is closed before continuing; otherwise, there are ways we + // could get into memory corruption. See https://github.com/simdjson/simdjson/issues/906 + if (!STREAMING) { + switch (*value) { + case '{': if (last_structural() != '}') { log_value("starting brace unmatched"); return TAPE_ERROR; }; break; + case '[': if (last_structural() != ']') { log_value("starting bracket unmatched"); return TAPE_ERROR; }; break; + } + } - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY(visitor.visit_empty_object(*this)); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY(visitor.visit_empty_array(*this)); break; } goto array_begin; - default: SIMDJSON_TRY(visitor.visit_root_primitive(*this, value)); break; - } - } - goto document_end; + switch (*value) { + case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; + case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; + default: SIMDJSON_TRY( visitor.visit_root_primitive(*this, value) ); break; + } + } + goto document_end; - // - // Object parser states - // - object_begin: - log_start_value("object"); - depth++; - if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } - dom_parser.is_array[depth] = false; - SIMDJSON_TRY(visitor.visit_object_start(*this)); +// +// Object parser states +// +object_begin: + log_start_value("object"); + depth++; + if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } + dom_parser.is_array[depth] = false; + SIMDJSON_TRY( visitor.visit_object_start(*this) ); - { - auto key = advance(); - if (*key != '"') { log_error("Object does not start with a key"); return TAPE_ERROR; } - SIMDJSON_TRY(visitor.increment_count(*this)); - SIMDJSON_TRY(visitor.visit_key(*this, key)); - } + { + auto key = advance(); + if (*key != '"') { log_error("Object does not start with a key"); return TAPE_ERROR; } + SIMDJSON_TRY( visitor.increment_count(*this) ); + SIMDJSON_TRY( visitor.visit_key(*this, key) ); + } - object_field: - if (simdjson_unlikely(*advance() != ':')) { log_error("Missing colon after key in object"); return TAPE_ERROR; } - { - auto value = advance(); - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY(visitor.visit_empty_object(*this)); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY(visitor.visit_empty_array(*this)); break; } goto array_begin; - default: SIMDJSON_TRY(visitor.visit_primitive(*this, value)); break; - } - } +object_field: + if (simdjson_unlikely( *advance() != ':' )) { log_error("Missing colon after key in object"); return TAPE_ERROR; } + { + auto value = advance(); + switch (*value) { + case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; + case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; + default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break; + } + } - object_continue: - switch (*advance()) { - case ',': - SIMDJSON_TRY(visitor.increment_count(*this)); - { - auto key = advance(); - if (simdjson_unlikely(*key != '"')) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; } - SIMDJSON_TRY(visitor.visit_key(*this, key)); - } - goto object_field; - case '}': log_end_value("object"); SIMDJSON_TRY(visitor.visit_object_end(*this)); goto scope_end; - default: log_error("No comma between object fields"); return TAPE_ERROR; - } +object_continue: + switch (*advance()) { + case ',': + SIMDJSON_TRY( visitor.increment_count(*this) ); + { + auto key = advance(); + if (simdjson_unlikely( *key != '"' )) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; } + SIMDJSON_TRY( visitor.visit_key(*this, key) ); + } + goto object_field; + case '}': log_end_value("object"); SIMDJSON_TRY( visitor.visit_object_end(*this) ); goto scope_end; + default: log_error("No comma between object fields"); return TAPE_ERROR; + } - scope_end: - depth--; - if (depth == 0) { goto document_end; } - if (dom_parser.is_array[depth]) { goto array_continue; } - goto object_continue; +scope_end: + depth--; + if (depth == 0) { goto document_end; } + if (dom_parser.is_array[depth]) { goto array_continue; } + goto object_continue; - // - // Array parser states - // - array_begin: - log_start_value("array"); - depth++; - if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } - dom_parser.is_array[depth] = true; - SIMDJSON_TRY(visitor.visit_array_start(*this)); - SIMDJSON_TRY(visitor.increment_count(*this)); +// +// Array parser states +// +array_begin: + log_start_value("array"); + depth++; + if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } + dom_parser.is_array[depth] = true; + SIMDJSON_TRY( visitor.visit_array_start(*this) ); + SIMDJSON_TRY( visitor.increment_count(*this) ); - array_value: - { - auto value = advance(); - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY(visitor.visit_empty_object(*this)); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY(visitor.visit_empty_array(*this)); break; } goto array_begin; - default: SIMDJSON_TRY(visitor.visit_primitive(*this, value)); break; - } - } +array_value: + { + auto value = advance(); + switch (*value) { + case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; + case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; + default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break; + } + } - array_continue: - switch (*advance()) { - case ',': SIMDJSON_TRY(visitor.increment_count(*this)); goto array_value; - case ']': log_end_value("array"); SIMDJSON_TRY(visitor.visit_array_end(*this)); goto scope_end; - default: log_error("Missing comma between array values"); return TAPE_ERROR; - } +array_continue: + switch (*advance()) { + case ',': SIMDJSON_TRY( visitor.increment_count(*this) ); goto array_value; + case ']': log_end_value("array"); SIMDJSON_TRY( visitor.visit_array_end(*this) ); goto scope_end; + default: log_error("Missing comma between array values"); return TAPE_ERROR; + } - document_end: - log_end_value("document"); - SIMDJSON_TRY(visitor.visit_document_end(*this)); +document_end: + log_end_value("document"); + SIMDJSON_TRY( visitor.visit_document_end(*this) ); - dom_parser.next_structural_index = uint32_t(next_structural - &dom_parser.structural_indexes[0]); + dom_parser.next_structural_index = uint32_t(next_structural - &dom_parser.structural_indexes[0]); - // If we didn't make it to the end, it's an error - if (!STREAMING && dom_parser.next_structural_index != dom_parser.n_structural_indexes) { - log_error("More than one JSON value at the root of the document, or extra characters at the end of the JSON!"); - return TAPE_ERROR; - } + // If we didn't make it to the end, it's an error + if ( !STREAMING && dom_parser.next_structural_index != dom_parser.n_structural_indexes ) { + log_error("More than one JSON value at the root of the document, or extra characters at the end of the JSON!"); + return TAPE_ERROR; + } - return SUCCESS; + return SUCCESS; - } // walk_document() +} // walk_document() - simdjson_inline json_iterator::json_iterator(dom_parser_implementation& _dom_parser, size_t start_structural_index) - : buf{ _dom_parser.buf }, - next_structural{ &_dom_parser.structural_indexes[start_structural_index] }, - dom_parser{ _dom_parser } { - } +simdjson_inline json_iterator::json_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index) + : buf{_dom_parser.buf}, + next_structural{&_dom_parser.structural_indexes[start_structural_index]}, + dom_parser{_dom_parser} { +} - simdjson_inline const uint8_t* json_iterator::peek() const noexcept { - return &buf[*(next_structural)]; - } - simdjson_inline const uint8_t* json_iterator::advance() noexcept { - return &buf[*(next_structural++)]; - } - simdjson_inline size_t json_iterator::remaining_len() const noexcept { - return dom_parser.len - *(next_structural - 1); - } +simdjson_inline const uint8_t *json_iterator::peek() const noexcept { + return &buf[*(next_structural)]; +} +simdjson_inline const uint8_t *json_iterator::advance() noexcept { + return &buf[*(next_structural++)]; +} +simdjson_inline size_t json_iterator::remaining_len() const noexcept { + return dom_parser.len - *(next_structural-1); +} - simdjson_inline bool json_iterator::at_eof() const noexcept { - return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; - } - simdjson_inline bool json_iterator::at_beginning() const noexcept { - return next_structural == dom_parser.structural_indexes.get(); - } - simdjson_inline uint8_t json_iterator::last_structural() const noexcept { - return buf[dom_parser.structural_indexes[dom_parser.n_structural_indexes - 1]]; - } +simdjson_inline bool json_iterator::at_eof() const noexcept { + return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; +} +simdjson_inline bool json_iterator::at_beginning() const noexcept { + return next_structural == dom_parser.structural_indexes.get(); +} +simdjson_inline uint8_t json_iterator::last_structural() const noexcept { + return buf[dom_parser.structural_indexes[dom_parser.n_structural_indexes - 1]]; +} - simdjson_inline void json_iterator::log_value(const char* type) const noexcept { - logger::log_line(*this, "", type, ""); - } +simdjson_inline void json_iterator::log_value(const char *type) const noexcept { + logger::log_line(*this, "", type, ""); +} - simdjson_inline void json_iterator::log_start_value(const char* type) const noexcept { - logger::log_line(*this, "+", type, ""); - if (logger::LOG_ENABLED) { logger::log_depth++; } - } +simdjson_inline void json_iterator::log_start_value(const char *type) const noexcept { + logger::log_line(*this, "+", type, ""); + if (logger::LOG_ENABLED) { logger::log_depth++; } +} - simdjson_inline void json_iterator::log_end_value(const char* type) const noexcept { - if (logger::LOG_ENABLED) { logger::log_depth--; } - logger::log_line(*this, "-", type, ""); - } +simdjson_inline void json_iterator::log_end_value(const char *type) const noexcept { + if (logger::LOG_ENABLED) { logger::log_depth--; } + logger::log_line(*this, "-", type, ""); +} - simdjson_inline void json_iterator::log_error(const char* error) const noexcept { - logger::log_line(*this, "", "ERROR", error); - } +simdjson_inline void json_iterator::log_error(const char *error) const noexcept { + logger::log_line(*this, "", "ERROR", error); +} - template - simdjson_warn_unused simdjson_inline error_code json_iterator::visit_root_primitive(V& visitor, const uint8_t* value) noexcept { - switch (*value) { - case '"': return visitor.visit_root_string(*this, value); - case 't': return visitor.visit_root_true_atom(*this, value); - case 'f': return visitor.visit_root_false_atom(*this, value); - case 'n': return visitor.visit_root_null_atom(*this, value); - case '-': - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - return visitor.visit_root_number(*this, value); - default: - log_error("Document starts with a non-value character"); - return TAPE_ERROR; - } - } - template - simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V& visitor, const uint8_t* value) noexcept { - // Use the fact that most scalars are going to be either strings or numbers. - if (*value == '"') { - return visitor.visit_string(*this, value); - } - else if (((*value - '0') < 10) || (*value == '-')) { - return visitor.visit_number(*this, value); - } - // true, false, null are uncommon. - switch (*value) { - case 't': return visitor.visit_true_atom(*this, value); - case 'f': return visitor.visit_false_atom(*this, value); - case 'n': return visitor.visit_null_atom(*this, value); - default: - log_error("Non-value found when value was expected!"); - return TAPE_ERROR; - } - } +template +simdjson_warn_unused simdjson_inline error_code json_iterator::visit_root_primitive(V &visitor, const uint8_t *value) noexcept { + switch (*value) { + case '"': return visitor.visit_root_string(*this, value); + case 't': return visitor.visit_root_true_atom(*this, value); + case 'f': return visitor.visit_root_false_atom(*this, value); + case 'n': return visitor.visit_root_null_atom(*this, value); + case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return visitor.visit_root_number(*this, value); + default: + log_error("Document starts with a non-value character"); + return TAPE_ERROR; + } +} +template +simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V &visitor, const uint8_t *value) noexcept { + // Use the fact that most scalars are going to be either strings or numbers. + if(*value == '"') { + return visitor.visit_string(*this, value); + } else if (((*value - '0') < 10) || (*value == '-')) { + return visitor.visit_number(*this, value); + } + // true, false, null are uncommon. + switch (*value) { + case 't': return visitor.visit_true_atom(*this, value); + case 'f': return visitor.visit_false_atom(*this, value); + case 'n': return visitor.visit_null_atom(*this, value); + default: + log_error("Non-value found when value was expected!"); + return TAPE_ERROR; + } +} - } // namespace stage2 - } // unnamed namespace - } // namespace arm64 +} // namespace stage2 +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_JSON_ITERATOR_H /* end file generic/stage2/json_iterator.h for arm64 */ /* including generic/stage2/stringparsing.h for arm64: #include */ /* begin file generic/stage2/stringparsing.h for arm64 */ +#include #ifndef SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -13372,242 +14701,238 @@ namespace simdjson { // It is intended to be included multiple times and compiled multiple times namespace simdjson { - namespace arm64 { - namespace { - /// @private - namespace stringparsing { +namespace arm64 { +namespace { +/// @private +namespace stringparsing { - // begin copypasta - // These chars yield themselves: " \ / - // b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab - // u not handled in this table as it's complex - static const uint8_t escape_map[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// begin copypasta +// These chars yield themselves: " \ / +// b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab +// u not handled in this table as it's complex +static const uint8_t escape_map[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0. + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5. - 0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6. - 0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7. + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4. + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5. + 0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6. + 0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - }; + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; - // handle a unicode codepoint - // write appropriate values into dest - // src will advance 6 bytes or 12 bytes - // dest will advance a variable amount (return via pointer) - // return true if the unicode codepoint was valid - // We work in little-endian then swap at write time - simdjson_warn_unused - simdjson_inline bool handle_unicode_codepoint(const uint8_t** src_ptr, - uint8_t** dst_ptr, bool allow_replacement) { - // Use the default Unicode Character 'REPLACEMENT CHARACTER' (U+FFFD) - constexpr uint32_t substitution_code_point = 0xfffd; - // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the - // conversion is not valid; we defer the check for this to inside the - // multilingual plane check. - uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); - *src_ptr += 6; +// handle a unicode codepoint +// write appropriate values into dest +// src will advance 6 bytes or 12 bytes +// dest will advance a variable amount (return via pointer) +// return true if the unicode codepoint was valid +// We work in little-endian then swap at write time +simdjson_warn_unused +simdjson_inline bool handle_unicode_codepoint(const uint8_t **src_ptr, + uint8_t **dst_ptr, bool allow_replacement) { + // Use the default Unicode Character 'REPLACEMENT CHARACTER' (U+FFFD) + constexpr uint32_t substitution_code_point = 0xfffd; + // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the + // conversion is not valid; we defer the check for this to inside the + // multilingual plane check. + uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); + *src_ptr += 6; - // If we found a high surrogate, we must - // check for low surrogate for characters - // outside the Basic - // Multilingual Plane. - if (code_point >= 0xd800 && code_point < 0xdc00) { - const uint8_t* src_data = *src_ptr; - /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ - if (((src_data[0] << 8) | src_data[1]) != ((static_cast ('\\') << 8) | static_cast ('u'))) { - if (!allow_replacement) { return false; } - code_point = substitution_code_point; - } - else { - uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); + // If we found a high surrogate, we must + // check for low surrogate for characters + // outside the Basic + // Multilingual Plane. + if (code_point >= 0xd800 && code_point < 0xdc00) { + const uint8_t *src_data = *src_ptr; + /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ + if (((src_data[0] << 8) | src_data[1]) != ((static_cast ('\\') << 8) | static_cast ('u'))) { + if(!allow_replacement) { return false; } + code_point = substitution_code_point; + } else { + uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); - // We have already checked that the high surrogate is valid and - // (code_point - 0xd800) < 1024. - // - // Check that code_point_2 is in the range 0xdc00..0xdfff - // and that code_point_2 was parsed from valid hex. - uint32_t low_bit = code_point_2 - 0xdc00; - if (low_bit >> 10) { - if (!allow_replacement) { return false; } - code_point = substitution_code_point; - } - else { - code_point = (((code_point - 0xd800) << 10) | low_bit) + 0x10000; - *src_ptr += 6; - } + // We have already checked that the high surrogate is valid and + // (code_point - 0xd800) < 1024. + // + // Check that code_point_2 is in the range 0xdc00..0xdfff + // and that code_point_2 was parsed from valid hex. + uint32_t low_bit = code_point_2 - 0xdc00; + if (low_bit >> 10) { + if(!allow_replacement) { return false; } + code_point = substitution_code_point; + } else { + code_point = (((code_point - 0xd800) << 10) | low_bit) + 0x10000; + *src_ptr += 6; + } - } - } - else if (code_point >= 0xdc00 && code_point <= 0xdfff) { - // If we encounter a low surrogate (not preceded by a high surrogate) - // then we have an error. - if (!allow_replacement) { return false; } - code_point = substitution_code_point; - } - size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); - *dst_ptr += offset; - return offset > 0; - } + } + } else if (code_point >= 0xdc00 && code_point <= 0xdfff) { + // If we encounter a low surrogate (not preceded by a high surrogate) + // then we have an error. + if(!allow_replacement) { return false; } + code_point = substitution_code_point; + } + size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); + *dst_ptr += offset; + return offset > 0; +} - // handle a unicode codepoint using the wobbly convention - // https://simonsapin.github.io/wtf-8/ - // write appropriate values into dest - // src will advance 6 bytes or 12 bytes - // dest will advance a variable amount (return via pointer) - // return true if the unicode codepoint was valid - // We work in little-endian then swap at write time - simdjson_warn_unused - simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t** src_ptr, - uint8_t** dst_ptr) { - // It is not ideal that this function is nearly identical to handle_unicode_codepoint. - // - // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the - // conversion is not valid; we defer the check for this to inside the - // multilingual plane check. - uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); - *src_ptr += 6; - // If we found a high surrogate, we must - // check for low surrogate for characters - // outside the Basic - // Multilingual Plane. - if (code_point >= 0xd800 && code_point < 0xdc00) { - const uint8_t* src_data = *src_ptr; - /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ - if (((src_data[0] << 8) | src_data[1]) == ((static_cast ('\\') << 8) | static_cast ('u'))) { - uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); - uint32_t low_bit = code_point_2 - 0xdc00; - if ((low_bit >> 10) == 0) { - code_point = - (((code_point - 0xd800) << 10) | low_bit) + 0x10000; - *src_ptr += 6; - } - } - } +// handle a unicode codepoint using the wobbly convention +// https://simonsapin.github.io/wtf-8/ +// write appropriate values into dest +// src will advance 6 bytes or 12 bytes +// dest will advance a variable amount (return via pointer) +// return true if the unicode codepoint was valid +// We work in little-endian then swap at write time +simdjson_warn_unused +simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t **src_ptr, + uint8_t **dst_ptr) { + // It is not ideal that this function is nearly identical to handle_unicode_codepoint. + // + // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the + // conversion is not valid; we defer the check for this to inside the + // multilingual plane check. + uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); + *src_ptr += 6; + // If we found a high surrogate, we must + // check for low surrogate for characters + // outside the Basic + // Multilingual Plane. + if (code_point >= 0xd800 && code_point < 0xdc00) { + const uint8_t *src_data = *src_ptr; + /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ + if (((src_data[0] << 8) | src_data[1]) == ((static_cast ('\\') << 8) | static_cast ('u'))) { + uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); + uint32_t low_bit = code_point_2 - 0xdc00; + if ((low_bit >> 10) == 0) { + code_point = + (((code_point - 0xd800) << 10) | low_bit) + 0x10000; + *src_ptr += 6; + } + } + } - size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); - *dst_ptr += offset; - return offset > 0; - } + size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); + *dst_ptr += offset; + return offset > 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. - */ - simdjson_warn_unused simdjson_inline uint8_t* parse_string(const uint8_t* src, uint8_t* dst, bool allow_replacement) { - while (1) { - // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); - // If the next thing is the end quote, copy and return - if (bs_quote.has_quote_first()) { - // we encountered quotes first. Move dst to point to quotes and exit - return dst + bs_quote.quote_index(); - } - if (bs_quote.has_backslash()) { - /* find out where the backspace is */ - auto bs_dist = bs_quote.backslash_index(); - uint8_t escape_char = src[bs_dist + 1]; - /* we encountered backslash first. Handle backslash */ - if (escape_char == 'u') { - /* move src/dst up to the start; they will be further adjusted - within the unicode codepoint handling code. */ - src += bs_dist; - dst += bs_dist; - if (!handle_unicode_codepoint(&src, &dst, allow_replacement)) { - return nullptr; - } - } - else { - /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and - * write bs_dist+1 characters to output - * note this may reach beyond the part of the buffer we've actually - * seen. I think this is ok */ - uint8_t escape_result = escape_map[escape_char]; - if (escape_result == 0u) { - return nullptr; /* bogus escape value is an error */ - } - dst[bs_dist] = escape_result; - src += bs_dist + 2; - dst += bs_dist + 1; - } - } - else { - /* they are the same. Since they can't co-occur, it means we - * encountered neither. */ - src += backslash_and_quote::BYTES_PROCESSED; - dst += backslash_and_quote::BYTES_PROCESSED; - } - } - } +/** + * 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. + */ +simdjson_warn_unused simdjson_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) { + while (1) { + // Copy the next n bytes, and find the backslash and quote in them. + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); + // If the next thing is the end quote, copy and return + if (bs_quote.has_quote_first()) { + // we encountered quotes first. Move dst to point to quotes and exit + return dst + bs_quote.quote_index(); + } + if (bs_quote.has_backslash()) { + /* find out where the backspace is */ + auto bs_dist = bs_quote.backslash_index(); + uint8_t escape_char = src[bs_dist + 1]; + /* we encountered backslash first. Handle backslash */ + if (escape_char == 'u') { + /* move src/dst up to the start; they will be further adjusted + within the unicode codepoint handling code. */ + src += bs_dist; + dst += bs_dist; + if (!handle_unicode_codepoint(&src, &dst, allow_replacement)) { + return nullptr; + } + } else { + /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and + * write bs_dist+1 characters to output + * note this may reach beyond the part of the buffer we've actually + * seen. I think this is ok */ + uint8_t escape_result = escape_map[escape_char]; + if (escape_result == 0u) { + return nullptr; /* bogus escape value is an error */ + } + dst[bs_dist] = escape_result; + src += bs_dist + 2; + dst += bs_dist + 1; + } + } else { + /* they are the same. Since they can't co-occur, it means we + * encountered neither. */ + src += backslash_and_quote::BYTES_PROCESSED; + dst += backslash_and_quote::BYTES_PROCESSED; + } + } +} - simdjson_warn_unused simdjson_inline uint8_t* parse_wobbly_string(const uint8_t* src, uint8_t* dst) { - // It is not ideal that this function is nearly identical to parse_string. - while (1) { - // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); - // If the next thing is the end quote, copy and return - if (bs_quote.has_quote_first()) { - // we encountered quotes first. Move dst to point to quotes and exit - return dst + bs_quote.quote_index(); - } - if (bs_quote.has_backslash()) { - /* find out where the backspace is */ - auto bs_dist = bs_quote.backslash_index(); - uint8_t escape_char = src[bs_dist + 1]; - /* we encountered backslash first. Handle backslash */ - if (escape_char == 'u') { - /* move src/dst up to the start; they will be further adjusted - within the unicode codepoint handling code. */ - src += bs_dist; - dst += bs_dist; - if (!handle_unicode_codepoint_wobbly(&src, &dst)) { - return nullptr; - } - } - else { - /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and - * write bs_dist+1 characters to output - * note this may reach beyond the part of the buffer we've actually - * seen. I think this is ok */ - uint8_t escape_result = escape_map[escape_char]; - if (escape_result == 0u) { - return nullptr; /* bogus escape value is an error */ - } - dst[bs_dist] = escape_result; - src += bs_dist + 2; - dst += bs_dist + 1; - } - } - else { - /* they are the same. Since they can't co-occur, it means we - * encountered neither. */ - src += backslash_and_quote::BYTES_PROCESSED; - dst += backslash_and_quote::BYTES_PROCESSED; - } - } - } +simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) { + // It is not ideal that this function is nearly identical to parse_string. + while (1) { + // Copy the next n bytes, and find the backslash and quote in them. + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); + // If the next thing is the end quote, copy and return + if (bs_quote.has_quote_first()) { + // we encountered quotes first. Move dst to point to quotes and exit + return dst + bs_quote.quote_index(); + } + if (bs_quote.has_backslash()) { + /* find out where the backspace is */ + auto bs_dist = bs_quote.backslash_index(); + uint8_t escape_char = src[bs_dist + 1]; + /* we encountered backslash first. Handle backslash */ + if (escape_char == 'u') { + /* move src/dst up to the start; they will be further adjusted + within the unicode codepoint handling code. */ + src += bs_dist; + dst += bs_dist; + if (!handle_unicode_codepoint_wobbly(&src, &dst)) { + return nullptr; + } + } else { + /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and + * write bs_dist+1 characters to output + * note this may reach beyond the part of the buffer we've actually + * seen. I think this is ok */ + uint8_t escape_result = escape_map[escape_char]; + if (escape_result == 0u) { + return nullptr; /* bogus escape value is an error */ + } + dst[bs_dist] = escape_result; + src += bs_dist + 2; + dst += bs_dist + 1; + } + } else { + /* they are the same. Since they can't co-occur, it means we + * encountered neither. */ + src += backslash_and_quote::BYTES_PROCESSED; + dst += backslash_and_quote::BYTES_PROCESSED; + } + } +} - } // namespace stringparsing - } // unnamed namespace - } // namespace arm64 +} // namespace stringparsing + +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H @@ -13623,58 +14948,58 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { - namespace { - namespace stage2 { +namespace arm64 { +namespace { +namespace stage2 { - class structural_iterator { - public: - const uint8_t* const buf; - uint32_t* next_structural; - dom_parser_implementation& dom_parser; +class structural_iterator { +public: + const uint8_t* const buf; + uint32_t *next_structural; + dom_parser_implementation &dom_parser; - // Start a structural - simdjson_inline structural_iterator(dom_parser_implementation& _dom_parser, size_t start_structural_index) - : buf{ _dom_parser.buf }, - next_structural{ &_dom_parser.structural_indexes[start_structural_index] }, - dom_parser{ _dom_parser } { - } - // Get the buffer position of the current structural character - simdjson_inline const uint8_t* current() { - return &buf[*(next_structural - 1)]; - } - // Get the current structural character - simdjson_inline char current_char() { - return buf[*(next_structural - 1)]; - } - // Get the next structural character without advancing - simdjson_inline char peek_next_char() { - return buf[*next_structural]; - } - simdjson_inline const uint8_t* peek() { - return &buf[*next_structural]; - } - simdjson_inline const uint8_t* advance() { - return &buf[*(next_structural++)]; - } - simdjson_inline char advance_char() { - return buf[*(next_structural++)]; - } - simdjson_inline size_t remaining_len() { - return dom_parser.len - *(next_structural - 1); - } + // Start a structural + simdjson_inline structural_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index) + : buf{_dom_parser.buf}, + next_structural{&_dom_parser.structural_indexes[start_structural_index]}, + dom_parser{_dom_parser} { + } + // Get the buffer position of the current structural character + simdjson_inline const uint8_t* current() { + return &buf[*(next_structural-1)]; + } + // Get the current structural character + simdjson_inline char current_char() { + return buf[*(next_structural-1)]; + } + // Get the next structural character without advancing + simdjson_inline char peek_next_char() { + return buf[*next_structural]; + } + simdjson_inline const uint8_t* peek() { + return &buf[*next_structural]; + } + simdjson_inline const uint8_t* advance() { + return &buf[*(next_structural++)]; + } + simdjson_inline char advance_char() { + return buf[*(next_structural++)]; + } + simdjson_inline size_t remaining_len() { + return dom_parser.len - *(next_structural-1); + } - simdjson_inline bool at_end() { - return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; - } - simdjson_inline bool at_beginning() { - return next_structural == dom_parser.structural_indexes.get(); - } - }; + simdjson_inline bool at_end() { + return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; + } + simdjson_inline bool at_beginning() { + return next_structural == dom_parser.structural_indexes.get(); + } +}; - } // namespace stage2 - } // unnamed namespace - } // namespace arm64 +} // namespace stage2 +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_STRUCTURAL_ITERATOR_H @@ -13697,284 +15022,284 @@ namespace simdjson { namespace simdjson { - namespace arm64 { - namespace { - namespace stage2 { +namespace arm64 { +namespace { +namespace stage2 { - struct tape_builder { - template - simdjson_warn_unused static simdjson_inline error_code parse_document( - dom_parser_implementation& dom_parser, - dom::document& doc) noexcept; +struct tape_builder { + template + simdjson_warn_unused static simdjson_inline error_code parse_document( + dom_parser_implementation &dom_parser, + dom::document &doc) noexcept; - /** Called when a non-empty document starts. */ - simdjson_warn_unused simdjson_inline error_code visit_document_start(json_iterator& iter) noexcept; - /** Called when a non-empty document ends without error. */ - simdjson_warn_unused simdjson_inline error_code visit_document_end(json_iterator& iter) noexcept; + /** Called when a non-empty document starts. */ + simdjson_warn_unused simdjson_inline error_code visit_document_start(json_iterator &iter) noexcept; + /** Called when a non-empty document ends without error. */ + simdjson_warn_unused simdjson_inline error_code visit_document_end(json_iterator &iter) noexcept; - /** Called when a non-empty array starts. */ - simdjson_warn_unused simdjson_inline error_code visit_array_start(json_iterator& iter) noexcept; - /** Called when a non-empty array ends. */ - simdjson_warn_unused simdjson_inline error_code visit_array_end(json_iterator& iter) noexcept; - /** Called when an empty array is found. */ - simdjson_warn_unused simdjson_inline error_code visit_empty_array(json_iterator& iter) noexcept; + /** Called when a non-empty array starts. */ + simdjson_warn_unused simdjson_inline error_code visit_array_start(json_iterator &iter) noexcept; + /** Called when a non-empty array ends. */ + simdjson_warn_unused simdjson_inline error_code visit_array_end(json_iterator &iter) noexcept; + /** Called when an empty array is found. */ + simdjson_warn_unused simdjson_inline error_code visit_empty_array(json_iterator &iter) noexcept; - /** Called when a non-empty object starts. */ - simdjson_warn_unused simdjson_inline error_code visit_object_start(json_iterator& iter) noexcept; - /** - * Called when a key in a field is encountered. - * - * primitive, visit_object_start, visit_empty_object, visit_array_start, or visit_empty_array - * will be called after this with the field value. - */ - simdjson_warn_unused simdjson_inline error_code visit_key(json_iterator& iter, const uint8_t* key) noexcept; - /** Called when a non-empty object ends. */ - simdjson_warn_unused simdjson_inline error_code visit_object_end(json_iterator& iter) noexcept; - /** Called when an empty object is found. */ - simdjson_warn_unused simdjson_inline error_code visit_empty_object(json_iterator& iter) noexcept; + /** Called when a non-empty object starts. */ + simdjson_warn_unused simdjson_inline error_code visit_object_start(json_iterator &iter) noexcept; + /** + * Called when a key in a field is encountered. + * + * primitive, visit_object_start, visit_empty_object, visit_array_start, or visit_empty_array + * will be called after this with the field value. + */ + simdjson_warn_unused simdjson_inline error_code visit_key(json_iterator &iter, const uint8_t *key) noexcept; + /** Called when a non-empty object ends. */ + simdjson_warn_unused simdjson_inline error_code visit_object_end(json_iterator &iter) noexcept; + /** Called when an empty object is found. */ + simdjson_warn_unused simdjson_inline error_code visit_empty_object(json_iterator &iter) noexcept; - /** - * Called when a string, number, boolean or null is found. - */ - simdjson_warn_unused simdjson_inline error_code visit_primitive(json_iterator& iter, const uint8_t* value) noexcept; - /** - * Called when a string, number, boolean or null is found at the top level of a document (i.e. - * when there is no array or object and the entire document is a single string, number, boolean or - * null. - * - * This is separate from primitive() because simdjson's normal primitive parsing routines assume - * there is at least one more token after the value, which is only true in an array or object. - */ - simdjson_warn_unused simdjson_inline error_code visit_root_primitive(json_iterator& iter, const uint8_t* value) noexcept; + /** + * Called when a string, number, boolean or null is found. + */ + simdjson_warn_unused simdjson_inline error_code visit_primitive(json_iterator &iter, const uint8_t *value) noexcept; + /** + * Called when a string, number, boolean or null is found at the top level of a document (i.e. + * when there is no array or object and the entire document is a single string, number, boolean or + * null. + * + * This is separate from primitive() because simdjson's normal primitive parsing routines assume + * there is at least one more token after the value, which is only true in an array or object. + */ + simdjson_warn_unused simdjson_inline error_code visit_root_primitive(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_string(json_iterator& iter, const uint8_t* value, bool key = false) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_number(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_true_atom(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_false_atom(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_null_atom(json_iterator& iter, const uint8_t* value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_string(json_iterator &iter, const uint8_t *value, bool key = false) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_number(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_true_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_false_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_null_atom(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_string(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_number(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_true_atom(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_false_atom(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_null_atom(json_iterator& iter, const uint8_t* value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_string(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_number(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_true_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept; - /** Called each time a new field or element in an array or object is found. */ - simdjson_warn_unused simdjson_inline error_code increment_count(json_iterator& iter) noexcept; + /** Called each time a new field or element in an array or object is found. */ + simdjson_warn_unused simdjson_inline error_code increment_count(json_iterator &iter) noexcept; - /** Next location to write to tape */ - tape_writer tape; - private: - /** Next write location in the string buf for stage 2 parsing */ - uint8_t* current_string_buf_loc; + /** Next location to write to tape */ + tape_writer tape; +private: + /** Next write location in the string buf for stage 2 parsing */ + uint8_t *current_string_buf_loc; - simdjson_inline tape_builder(dom::document& doc) noexcept; + simdjson_inline tape_builder(dom::document &doc) noexcept; - simdjson_inline uint32_t next_tape_index(json_iterator& iter) const noexcept; - simdjson_inline void start_container(json_iterator& iter) noexcept; - simdjson_warn_unused simdjson_inline error_code end_container(json_iterator& iter, internal::tape_type start, internal::tape_type end) noexcept; - simdjson_warn_unused simdjson_inline error_code empty_container(json_iterator& iter, internal::tape_type start, internal::tape_type end) noexcept; - simdjson_inline uint8_t* on_start_string(json_iterator& iter) noexcept; - simdjson_inline void on_end_string(uint8_t* dst) noexcept; - }; // struct tape_builder + simdjson_inline uint32_t next_tape_index(json_iterator &iter) const noexcept; + simdjson_inline void start_container(json_iterator &iter) noexcept; + simdjson_warn_unused simdjson_inline error_code end_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept; + simdjson_warn_unused simdjson_inline error_code empty_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept; + simdjson_inline uint8_t *on_start_string(json_iterator &iter) noexcept; + simdjson_inline void on_end_string(uint8_t *dst) noexcept; +}; // struct tape_builder - template - simdjson_warn_unused simdjson_inline error_code tape_builder::parse_document( - dom_parser_implementation& dom_parser, - dom::document& doc) noexcept { - dom_parser.doc = &doc; - json_iterator iter(dom_parser, STREAMING ? dom_parser.next_structural_index : 0); - tape_builder builder(doc); - return iter.walk_document(builder); - } +template +simdjson_warn_unused simdjson_inline error_code tape_builder::parse_document( + dom_parser_implementation &dom_parser, + dom::document &doc) noexcept { + dom_parser.doc = &doc; + json_iterator iter(dom_parser, STREAMING ? dom_parser.next_structural_index : 0); + tape_builder builder(doc); + return iter.walk_document(builder); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_primitive(json_iterator& iter, const uint8_t* value) noexcept { - return iter.visit_root_primitive(*this, value); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_primitive(json_iterator& iter, const uint8_t* value) noexcept { - return iter.visit_primitive(*this, value); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_object(json_iterator& iter) noexcept { - return empty_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_array(json_iterator& iter) noexcept { - return empty_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_primitive(json_iterator &iter, const uint8_t *value) noexcept { + return iter.visit_root_primitive(*this, value); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_primitive(json_iterator &iter, const uint8_t *value) noexcept { + return iter.visit_primitive(*this, value); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_object(json_iterator &iter) noexcept { + return empty_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_array(json_iterator &iter) noexcept { + return empty_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_start(json_iterator& iter) noexcept { - start_container(iter); - return SUCCESS; - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_start(json_iterator& iter) noexcept { - start_container(iter); - return SUCCESS; - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_start(json_iterator& iter) noexcept { - start_container(iter); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_start(json_iterator &iter) noexcept { + start_container(iter); + return SUCCESS; +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_start(json_iterator &iter) noexcept { + start_container(iter); + return SUCCESS; +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_start(json_iterator &iter) noexcept { + start_container(iter); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_end(json_iterator& iter) noexcept { - return end_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_end(json_iterator& iter) noexcept { - return end_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_end(json_iterator& iter) noexcept { - constexpr uint32_t start_tape_index = 0; - tape.append(start_tape_index, internal::tape_type::ROOT); - tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter), internal::tape_type::ROOT); - return SUCCESS; - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_key(json_iterator& iter, const uint8_t* key) noexcept { - return visit_string(iter, key, true); - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_end(json_iterator &iter) noexcept { + return end_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_end(json_iterator &iter) noexcept { + return end_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_end(json_iterator &iter) noexcept { + constexpr uint32_t start_tape_index = 0; + tape.append(start_tape_index, internal::tape_type::ROOT); + tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter), internal::tape_type::ROOT); + return SUCCESS; +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_key(json_iterator &iter, const uint8_t *key) noexcept { + return visit_string(iter, key, true); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::increment_count(json_iterator& iter) noexcept { - iter.dom_parser.open_containers[iter.depth].count++; // we have a key value pair in the object at parser.dom_parser.depth - 1 - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::increment_count(json_iterator &iter) noexcept { + iter.dom_parser.open_containers[iter.depth].count++; // we have a key value pair in the object at parser.dom_parser.depth - 1 + return SUCCESS; +} - simdjson_inline tape_builder::tape_builder(dom::document& doc) noexcept : tape{ doc.tape.get() }, current_string_buf_loc{ doc.string_buf.get() } {} +simdjson_inline tape_builder::tape_builder(dom::document &doc) noexcept : tape{doc.tape.get()}, current_string_buf_loc{doc.string_buf.get()} {} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_string(json_iterator& iter, const uint8_t* value, bool key) noexcept { - iter.log_value(key ? "key" : "string"); - uint8_t* dst = on_start_string(iter); - dst = stringparsing::parse_string(value + 1, dst, false); // We do not allow replacement when the escape characters are invalid. - if (dst == nullptr) { - iter.log_error("Invalid escape in string"); - return STRING_ERROR; - } - on_end_string(dst); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_string(json_iterator &iter, const uint8_t *value, bool key) noexcept { + iter.log_value(key ? "key" : "string"); + uint8_t *dst = on_start_string(iter); + dst = stringparsing::parse_string(value+1, dst, false); // We do not allow replacement when the escape characters are invalid. + if (dst == nullptr) { + iter.log_error("Invalid escape in string"); + return STRING_ERROR; + } + on_end_string(dst); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_string(json_iterator& iter, const uint8_t* value) noexcept { - return visit_string(iter, value); - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_string(json_iterator &iter, const uint8_t *value) noexcept { + return visit_string(iter, value); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_number(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("number"); - return numberparsing::parse_number(value, tape); - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_number(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("number"); + return numberparsing::parse_number(value, tape); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_number(json_iterator& iter, const uint8_t* value) noexcept { - // - // We need to make a copy to make sure that the string is space terminated. - // This is not about padding the input, which should already padded up - // to len + SIMDJSON_PADDING. However, we have no control at this stage - // on how the padding was done. What if the input string was padded with nulls? - // It is quite common for an input string to have an extra null character (C string). - // We do not want to allow 9\0 (where \0 is the null character) inside a JSON - // document, but the string "9\0" by itself is fine. So we make a copy and - // pad the input with spaces when we know that there is just one input element. - // This copy is relatively expensive, but it will almost never be called in - // practice unless you are in the strange scenario where you have many JSON - // documents made of single atoms. - // - std::unique_ptrcopy(new (std::nothrow) uint8_t[iter.remaining_len() + SIMDJSON_PADDING]); - if (copy.get() == nullptr) { return MEMALLOC; } - std::memcpy(copy.get(), value, iter.remaining_len()); - std::memset(copy.get() + iter.remaining_len(), ' ', SIMDJSON_PADDING); - error_code error = visit_number(iter, copy.get()); - return error; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_number(json_iterator &iter, const uint8_t *value) noexcept { + // + // We need to make a copy to make sure that the string is space terminated. + // This is not about padding the input, which should already padded up + // to len + SIMDJSON_PADDING. However, we have no control at this stage + // on how the padding was done. What if the input string was padded with nulls? + // It is quite common for an input string to have an extra null character (C string). + // We do not want to allow 9\0 (where \0 is the null character) inside a JSON + // document, but the string "9\0" by itself is fine. So we make a copy and + // pad the input with spaces when we know that there is just one input element. + // This copy is relatively expensive, but it will almost never be called in + // practice unless you are in the strange scenario where you have many JSON + // documents made of single atoms. + // + std::unique_ptrcopy(new (std::nothrow) uint8_t[iter.remaining_len() + SIMDJSON_PADDING]); + if (copy.get() == nullptr) { return MEMALLOC; } + std::memcpy(copy.get(), value, iter.remaining_len()); + std::memset(copy.get() + iter.remaining_len(), ' ', SIMDJSON_PADDING); + error_code error = visit_number(iter, copy.get()); + return error; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_true_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("true"); - if (!atomparsing::is_valid_true_atom(value)) { return T_ATOM_ERROR; } - tape.append(0, internal::tape_type::TRUE_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_true_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("true"); + if (!atomparsing::is_valid_true_atom(value)) { return T_ATOM_ERROR; } + tape.append(0, internal::tape_type::TRUE_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_true_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("true"); - if (!atomparsing::is_valid_true_atom(value, iter.remaining_len())) { return T_ATOM_ERROR; } - tape.append(0, internal::tape_type::TRUE_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_true_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("true"); + if (!atomparsing::is_valid_true_atom(value, iter.remaining_len())) { return T_ATOM_ERROR; } + tape.append(0, internal::tape_type::TRUE_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_false_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("false"); - if (!atomparsing::is_valid_false_atom(value)) { return F_ATOM_ERROR; } - tape.append(0, internal::tape_type::FALSE_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_false_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("false"); + if (!atomparsing::is_valid_false_atom(value)) { return F_ATOM_ERROR; } + tape.append(0, internal::tape_type::FALSE_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_false_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("false"); - if (!atomparsing::is_valid_false_atom(value, iter.remaining_len())) { return F_ATOM_ERROR; } - tape.append(0, internal::tape_type::FALSE_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("false"); + if (!atomparsing::is_valid_false_atom(value, iter.remaining_len())) { return F_ATOM_ERROR; } + tape.append(0, internal::tape_type::FALSE_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_null_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("null"); - if (!atomparsing::is_valid_null_atom(value)) { return N_ATOM_ERROR; } - tape.append(0, internal::tape_type::NULL_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_null_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("null"); + if (!atomparsing::is_valid_null_atom(value)) { return N_ATOM_ERROR; } + tape.append(0, internal::tape_type::NULL_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_null_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("null"); - if (!atomparsing::is_valid_null_atom(value, iter.remaining_len())) { return N_ATOM_ERROR; } - tape.append(0, internal::tape_type::NULL_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("null"); + if (!atomparsing::is_valid_null_atom(value, iter.remaining_len())) { return N_ATOM_ERROR; } + tape.append(0, internal::tape_type::NULL_VALUE); + return SUCCESS; +} - // private: +// private: - simdjson_inline uint32_t tape_builder::next_tape_index(json_iterator& iter) const noexcept { - return uint32_t(tape.next_tape_loc - iter.dom_parser.doc->tape.get()); - } +simdjson_inline uint32_t tape_builder::next_tape_index(json_iterator &iter) const noexcept { + return uint32_t(tape.next_tape_loc - iter.dom_parser.doc->tape.get()); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::empty_container(json_iterator& iter, internal::tape_type start, internal::tape_type end) noexcept { - auto start_index = next_tape_index(iter); - tape.append(start_index + 2, start); - tape.append(start_index, end); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::empty_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept { + auto start_index = next_tape_index(iter); + tape.append(start_index+2, start); + tape.append(start_index, end); + return SUCCESS; +} - simdjson_inline void tape_builder::start_container(json_iterator& iter) noexcept { - iter.dom_parser.open_containers[iter.depth].tape_index = next_tape_index(iter); - iter.dom_parser.open_containers[iter.depth].count = 0; - tape.skip(); // We don't actually *write* the start element until the end. - } +simdjson_inline void tape_builder::start_container(json_iterator &iter) noexcept { + iter.dom_parser.open_containers[iter.depth].tape_index = next_tape_index(iter); + iter.dom_parser.open_containers[iter.depth].count = 0; + tape.skip(); // We don't actually *write* the start element until the end. +} - simdjson_warn_unused simdjson_inline error_code tape_builder::end_container(json_iterator& iter, internal::tape_type start, internal::tape_type end) noexcept { - // Write the ending tape element, pointing at the start location - const uint32_t start_tape_index = iter.dom_parser.open_containers[iter.depth].tape_index; - tape.append(start_tape_index, end); - // Write the start tape element, pointing at the end location (and including count) - // count can overflow if it exceeds 24 bits... so we saturate - // the convention being that a cnt of 0xffffff or more is undetermined in value (>= 0xffffff). - const uint32_t count = iter.dom_parser.open_containers[iter.depth].count; - const uint32_t cntsat = count > 0xFFFFFF ? 0xFFFFFF : count; - tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter) | (uint64_t(cntsat) << 32), start); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::end_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept { + // Write the ending tape element, pointing at the start location + const uint32_t start_tape_index = iter.dom_parser.open_containers[iter.depth].tape_index; + tape.append(start_tape_index, end); + // Write the start tape element, pointing at the end location (and including count) + // count can overflow if it exceeds 24 bits... so we saturate + // the convention being that a cnt of 0xffffff or more is undetermined in value (>= 0xffffff). + const uint32_t count = iter.dom_parser.open_containers[iter.depth].count; + const uint32_t cntsat = count > 0xFFFFFF ? 0xFFFFFF : count; + tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter) | (uint64_t(cntsat) << 32), start); + return SUCCESS; +} - simdjson_inline uint8_t* tape_builder::on_start_string(json_iterator& iter) noexcept { - // we advance the point, accounting for the fact that we have a NULL termination - tape.append(current_string_buf_loc - iter.dom_parser.doc->string_buf.get(), internal::tape_type::STRING); - return current_string_buf_loc + sizeof(uint32_t); - } +simdjson_inline uint8_t *tape_builder::on_start_string(json_iterator &iter) noexcept { + // we advance the point, accounting for the fact that we have a NULL termination + tape.append(current_string_buf_loc - iter.dom_parser.doc->string_buf.get(), internal::tape_type::STRING); + return current_string_buf_loc + sizeof(uint32_t); +} - simdjson_inline void tape_builder::on_end_string(uint8_t* dst) noexcept { - uint32_t str_length = uint32_t(dst - (current_string_buf_loc + sizeof(uint32_t))); - // TODO check for overflow in case someone has a crazy string (>=4GB?) - // But only add the overflow check when the document itself exceeds 4GB - // Currently unneeded because we refuse to parse docs larger or equal to 4GB. - memcpy(current_string_buf_loc, &str_length, sizeof(uint32_t)); - // NULL termination is still handy if you expect all your strings to - // be NULL terminated? It comes at a small cost - *dst = 0; - current_string_buf_loc = dst + 1; - } +simdjson_inline void tape_builder::on_end_string(uint8_t *dst) noexcept { + uint32_t str_length = uint32_t(dst - (current_string_buf_loc + sizeof(uint32_t))); + // TODO check for overflow in case someone has a crazy string (>=4GB?) + // But only add the overflow check when the document itself exceeds 4GB + // Currently unneeded because we refuse to parse docs larger or equal to 4GB. + memcpy(current_string_buf_loc, &str_length, sizeof(uint32_t)); + // NULL termination is still handy if you expect all your strings to + // be NULL terminated? It comes at a small cost + *dst = 0; + current_string_buf_loc = dst + 1; +} - } // namespace stage2 - } // unnamed namespace - } // namespace arm64 +} // namespace stage2 +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_BUILDER_H @@ -13985,105 +15310,105 @@ namespace simdjson { // Stage 1 // namespace simdjson { - namespace arm64 { +namespace arm64 { - simdjson_warn_unused error_code implementation::create_dom_parser_implementation( - size_t capacity, - size_t max_depth, - std::unique_ptr& dst - ) const noexcept { - dst.reset(new (std::nothrow) dom_parser_implementation()); - if (!dst) { return MEMALLOC; } - if (auto err = dst->set_capacity(capacity)) - return err; - if (auto err = dst->set_max_depth(max_depth)) - return err; - return SUCCESS; - } +simdjson_warn_unused error_code implementation::create_dom_parser_implementation( + size_t capacity, + size_t max_depth, + std::unique_ptr& dst +) const noexcept { + dst.reset( new (std::nothrow) dom_parser_implementation() ); + if (!dst) { return MEMALLOC; } + if (auto err = dst->set_capacity(capacity)) + return err; + if (auto err = dst->set_max_depth(max_depth)) + return err; + return SUCCESS; +} - namespace { +namespace { - using namespace simd; +using namespace simd; - simdjson_inline json_character_block json_character_block::classify(const simd::simd8x64& in) { - // Functional programming causes trouble with Visual Studio. - // Keeping this version in comments since it is much nicer: - // auto v = in.map([&](simd8 chunk) { - // auto nib_lo = chunk & 0xf; - // auto nib_hi = chunk.shr<4>(); - // auto shuf_lo = nib_lo.lookup_16(16, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 1, 2, 9, 0, 0); - // auto shuf_hi = nib_hi.lookup_16(8, 0, 18, 4, 0, 1, 0, 1, 0, 0, 0, 3, 2, 1, 0, 0); - // return shuf_lo & shuf_hi; - // }); - const simd8 table1(16, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 1, 2, 9, 0, 0); - const simd8 table2(8, 0, 18, 4, 0, 1, 0, 1, 0, 0, 0, 3, 2, 1, 0, 0); +simdjson_inline json_character_block json_character_block::classify(const simd::simd8x64& in) { + // Functional programming causes trouble with Visual Studio. + // Keeping this version in comments since it is much nicer: + // auto v = in.map([&](simd8 chunk) { + // auto nib_lo = chunk & 0xf; + // auto nib_hi = chunk.shr<4>(); + // auto shuf_lo = nib_lo.lookup_16(16, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 1, 2, 9, 0, 0); + // auto shuf_hi = nib_hi.lookup_16(8, 0, 18, 4, 0, 1, 0, 1, 0, 0, 0, 3, 2, 1, 0, 0); + // return shuf_lo & shuf_hi; + // }); + const simd8 table1(16, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 1, 2, 9, 0, 0); + const simd8 table2(8, 0, 18, 4, 0, 1, 0, 1, 0, 0, 0, 3, 2, 1, 0, 0); - simd8x64 v( - (in.chunks[0] & 0xf).lookup_16(table1) & (in.chunks[0].shr<4>()).lookup_16(table2), - (in.chunks[1] & 0xf).lookup_16(table1) & (in.chunks[1].shr<4>()).lookup_16(table2), - (in.chunks[2] & 0xf).lookup_16(table1) & (in.chunks[2].shr<4>()).lookup_16(table2), - (in.chunks[3] & 0xf).lookup_16(table1) & (in.chunks[3].shr<4>()).lookup_16(table2) - ); + simd8x64 v( + (in.chunks[0] & 0xf).lookup_16(table1) & (in.chunks[0].shr<4>()).lookup_16(table2), + (in.chunks[1] & 0xf).lookup_16(table1) & (in.chunks[1].shr<4>()).lookup_16(table2), + (in.chunks[2] & 0xf).lookup_16(table1) & (in.chunks[2].shr<4>()).lookup_16(table2), + (in.chunks[3] & 0xf).lookup_16(table1) & (in.chunks[3].shr<4>()).lookup_16(table2) + ); - // We compute whitespace and op separately. If the code later only use one or the - // other, given the fact that all functions are aggressively inlined, we can - // hope that useless computations will be omitted. This is namely case when - // minifying (we only need whitespace). *However* if we only need spaces, - // it is likely that we will still compute 'v' above with two lookup_16: one - // could do it a bit cheaper. This is in contrast with the x64 implementations - // where we can, efficiently, do the white space and structural matching - // separately. One reason for this difference is that on ARM NEON, the table - // lookups either zero or leave unchanged the characters exceeding 0xF whereas - // on x64, the equivalent instruction (pshufb) automatically applies a mask, - // ignoring the 4 most significant bits. Thus the x64 implementation is - // optimized differently. This being said, if you use this code strictly - // just for minification (or just to identify the structural characters), - // there is a small untaken optimization opportunity here. We deliberately - // do not pick it up. + // We compute whitespace and op separately. If the code later only use one or the + // other, given the fact that all functions are aggressively inlined, we can + // hope that useless computations will be omitted. This is namely case when + // minifying (we only need whitespace). *However* if we only need spaces, + // it is likely that we will still compute 'v' above with two lookup_16: one + // could do it a bit cheaper. This is in contrast with the x64 implementations + // where we can, efficiently, do the white space and structural matching + // separately. One reason for this difference is that on ARM NEON, the table + // lookups either zero or leave unchanged the characters exceeding 0xF whereas + // on x64, the equivalent instruction (pshufb) automatically applies a mask, + // ignoring the 4 most significant bits. Thus the x64 implementation is + // optimized differently. This being said, if you use this code strictly + // just for minification (or just to identify the structural characters), + // there is a small untaken optimization opportunity here. We deliberately + // do not pick it up. - uint64_t op = simd8x64( - v.chunks[0].any_bits_set(0x7), - v.chunks[1].any_bits_set(0x7), - v.chunks[2].any_bits_set(0x7), - v.chunks[3].any_bits_set(0x7) - ).to_bitmask(); + uint64_t op = simd8x64( + v.chunks[0].any_bits_set(0x7), + v.chunks[1].any_bits_set(0x7), + v.chunks[2].any_bits_set(0x7), + v.chunks[3].any_bits_set(0x7) + ).to_bitmask(); - uint64_t whitespace = simd8x64( - v.chunks[0].any_bits_set(0x18), - v.chunks[1].any_bits_set(0x18), - v.chunks[2].any_bits_set(0x18), - v.chunks[3].any_bits_set(0x18) - ).to_bitmask(); + uint64_t whitespace = simd8x64( + v.chunks[0].any_bits_set(0x18), + v.chunks[1].any_bits_set(0x18), + v.chunks[2].any_bits_set(0x18), + v.chunks[3].any_bits_set(0x18) + ).to_bitmask(); - return { whitespace, op }; - } + return { whitespace, op }; +} - simdjson_inline bool is_ascii(const simd8x64& input) { - simd8 bits = input.reduce_or(); - return bits.max_val() < 0x80u; - } +simdjson_inline bool is_ascii(const simd8x64& input) { + simd8 bits = input.reduce_or(); + return bits.max_val() < 0x80u; +} - simdjson_unused simdjson_inline simd8 must_be_continuation(const simd8 prev1, const simd8 prev2, const simd8 prev3) { - simd8 is_second_byte = prev1 >= uint8_t(0xc0u); - simd8 is_third_byte = prev2 >= uint8_t(0xe0u); - simd8 is_fourth_byte = prev3 >= uint8_t(0xf0u); - // Use ^ instead of | for is_*_byte, because ^ is commutative, and the caller is using ^ as well. - // This will work fine because we only have to report errors for cases with 0-1 lead bytes. - // Multiple lead bytes implies 2 overlapping multibyte characters, and if that happens, there is - // guaranteed to be at least *one* lead byte that is part of only 1 other multibyte character. - // The error will be detected there. - return is_second_byte ^ is_third_byte ^ is_fourth_byte; - } +simdjson_unused simdjson_inline simd8 must_be_continuation(const simd8 prev1, const simd8 prev2, const simd8 prev3) { + simd8 is_second_byte = prev1 >= uint8_t(0xc0u); + simd8 is_third_byte = prev2 >= uint8_t(0xe0u); + simd8 is_fourth_byte = prev3 >= uint8_t(0xf0u); + // Use ^ instead of | for is_*_byte, because ^ is commutative, and the caller is using ^ as well. + // This will work fine because we only have to report errors for cases with 0-1 lead bytes. + // Multiple lead bytes implies 2 overlapping multibyte characters, and if that happens, there is + // guaranteed to be at least *one* lead byte that is part of only 1 other multibyte character. + // The error will be detected there. + return is_second_byte ^ is_third_byte ^ is_fourth_byte; +} - simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { - simd8 is_third_byte = prev2.saturating_sub(0xe0u - 0x80); // Only 111_____ will be >= 0x80 - simd8 is_fourth_byte = prev3.saturating_sub(0xf0u - 0x80); // Only 1111____ will be >= 0x80 - return is_third_byte | is_fourth_byte; - } +simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { + simd8 is_third_byte = prev2.saturating_sub(0xe0u-0x80); // Only 111_____ will be >= 0x80 + simd8 is_fourth_byte = prev3.saturating_sub(0xf0u-0x80); // Only 1111____ will be >= 0x80 + return is_third_byte | is_fourth_byte; +} - } // unnamed namespace - } // namespace arm64 +} // unnamed namespace +} // namespace arm64 } // namespace simdjson // @@ -14094,45 +15419,46 @@ namespace simdjson { // Implementation-specific overrides // namespace simdjson { - namespace arm64 { +namespace arm64 { - simdjson_warn_unused error_code implementation::minify(const uint8_t* buf, size_t len, uint8_t* dst, size_t& dst_len) const noexcept { - return arm64::stage1::json_minifier::minify<64>(buf, len, dst, dst_len); - } +simdjson_warn_unused error_code implementation::minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept { + return arm64::stage1::json_minifier::minify<64>(buf, len, dst, dst_len); +} - simdjson_warn_unused error_code dom_parser_implementation::stage1(const uint8_t* _buf, size_t _len, stage1_mode streaming) noexcept { - this->buf = _buf; - this->len = _len; - return arm64::stage1::json_structural_indexer::index<64>(buf, len, *this, streaming); - } +simdjson_warn_unused error_code dom_parser_implementation::stage1(const uint8_t *_buf, size_t _len, stage1_mode streaming) noexcept { + this->buf = _buf; + this->len = _len; + return arm64::stage1::json_structural_indexer::index<64>(buf, len, *this, streaming); +} - simdjson_warn_unused bool implementation::validate_utf8(const char* buf, size_t len) const noexcept { - return arm64::stage1::generic_validate_utf8(buf, len); - } +simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept { + return arm64::stage1::generic_validate_utf8(buf,len); +} - simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document& _doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); - } +simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept { + return stage2::tape_builder::parse_document(*this, _doc); +} - simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document& _doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); - } +simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept { + return stage2::tape_builder::parse_document(*this, _doc); +} - simdjson_warn_unused uint8_t* dom_parser_implementation::parse_string(const uint8_t* src, uint8_t* dst, bool allow_replacement) const noexcept { - return arm64::stringparsing::parse_string(src, dst, allow_replacement); - } +SIMDJSON_NO_SANITIZE_MEMORY +simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept { + return arm64::stringparsing::parse_string(src, dst, allow_replacement); +} - simdjson_warn_unused uint8_t* dom_parser_implementation::parse_wobbly_string(const uint8_t* src, uint8_t* dst) const noexcept { - return arm64::stringparsing::parse_wobbly_string(src, dst); - } +simdjson_warn_unused uint8_t *dom_parser_implementation::parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept { + return arm64::stringparsing::parse_wobbly_string(src, dst); +} - simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t* _buf, size_t _len, dom::document& _doc) noexcept { - auto error = stage1(_buf, _len, stage1_mode::regular); - if (error) { return error; } - return stage2(_doc); - } +simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept { + auto error = stage1(_buf, _len, stage1_mode::regular); + if (error) { return error; } + return stage2(_doc); +} - } // namespace arm64 +} // namespace arm64 } // namespace simdjson /* including simdjson/arm64/end.h: #include */ @@ -14180,21 +15506,21 @@ namespace simdjson { // 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 { +/** + * Implementation for Haswell (Intel AVX2). + */ +namespace haswell { - class implementation; +class implementation; - namespace { - namespace simd { - template struct simd8; - template struct simd8x64; - } // namespace simd - } // unnamed namespace +namespace { +namespace simd { +template struct simd8; +template struct simd8x64; +} // namespace simd +} // unnamed namespace - } // namespace haswell +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_HASWELL_BASE_H @@ -14242,8 +15568,8 @@ namespace simdjson { #include #include #include // for _mm_clmulepi64_si128 - // unfortunately, we may not get _blsr_u64, but, thankfully, clang - // has it as a macro. +// 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) @@ -14256,8 +15582,14 @@ static_assert(sizeof(__m256i) <= simdjson::SIMDJSON_PADDING, "insufficient paddi /* 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 */ @@ -14271,64 +15603,64 @@ SIMDJSON_TARGET_REGION("avx2,bmi,pclmul,lzcnt,popcnt") /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace { +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) { +// 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); + 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); + //////// + // 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 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)); - } +/* 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 - } +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); - } +simdjson_inline long long int count_ones(uint64_t input_num) { + return _popcnt64(input_num); +} #endif - simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, - uint64_t* result) { +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { #if SIMDJSON_REGULAR_VISUAL_STUDIO - return _addcarry_u64(0, value1, value2, - reinterpret_cast(result)); + return _addcarry_u64(0, value1, value2, + reinterpret_cast(result)); #else - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); #endif - } +} - } // unnamed namespace - } // namespace haswell +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_HASWELL_BITMANIPULATION_H @@ -14344,24 +15676,24 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace { +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); - } +// +// 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 +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_HASWELL_BITMASK_H @@ -14381,49 +15713,49 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace numberparsing { +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(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 */ +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(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; +/** @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; + // 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 + 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); + __uint128_t r = (static_cast<__uint128_t>(value1)) * value2; + answer.low = uint64_t(r); + answer.high = uint64_t(r >> 64); #endif - return answer; - } + return answer; +} - } // namespace numberparsing - } // namespace haswell +} // namespace numberparsing +} // namespace haswell } // namespace simdjson #define SIMDJSON_SWAR_NUMBER_PARSING 1 @@ -14443,364 +15775,365 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace { - namespace simd { +namespace haswell { +namespace { +namespace simd { - // Forward-declared so they can be used by splat and friends. - template - struct base { - __m256i value; + // Forward-declared so they can be used by splat and friends. + template + struct base { + __m256i value; - // Zero constructor - simdjson_inline base() : value{ __m256i() } {} + // Zero constructor + simdjson_inline base() : value{__m256i()} {} - // Conversion from SIMD register - simdjson_inline base(const __m256i _value) : value(_value) {} + // 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; } + // 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(this); *this_cast = *this_cast | other; return *this_cast; } - simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } - simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } - }; + // 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(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; - // Forward-declared so they can be used by splat and friends. - template - struct simd8; + // Forward-declared so they can be used by splat and friends. + template + struct simd8; - template> - struct base8 : base> { - typedef uint32_t bitmask_t; - typedef uint64_t bitmask2_t; + template> + struct base8: base> { + typedef uint32_t bitmask_t; + typedef uint64_t bitmask2_t; - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m256i _value) : base>(_value) {} + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m256i _value) : base>(_value) {} - friend simdjson_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm256_cmpeq_epi8(lhs, rhs); } + friend simdjson_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm256_cmpeq_epi8(lhs, rhs); } - static const int SIZE = sizeof(base::value); + static const int SIZE = sizeof(base::value); - template - simdjson_inline simd8 prev(const simd8 prev_chunk) const { - return _mm256_alignr_epi8(*this, _mm256_permute2x128_si256(prev_chunk, *this, 0x21), 16 - N); - } - }; + template + simdjson_inline simd8 prev(const simd8 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 : base8 { - static simdjson_inline simd8 splat(bool _value) { return _mm256_set1_epi8(uint8_t(-(!!_value))); } + // SIMD byte mask type (returned by things like eq and gt) + template<> + struct simd8: base8 { + static simdjson_inline simd8 splat(bool _value) { return _mm256_set1_epi8(uint8_t(-(!!_value))); } - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m256i _value) : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) : base8(splat(_value)) {} + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m256i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(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 operator~() const { return *this ^ true; } - }; + 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 operator~() const { return *this ^ true; } + }; - template - struct base8_numeric : base8 { - static simdjson_inline simd8 splat(T _value) { return _mm256_set1_epi8(_value); } - static simdjson_inline simd8 zero() { return _mm256_setzero_si256(); } - static simdjson_inline simd8 load(const T values[32]) { - return _mm256_loadu_si256(reinterpret_cast(values)); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdjson_inline simd8 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( - 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 - ); - } + template + struct base8_numeric: base8 { + static simdjson_inline simd8 splat(T _value) { return _mm256_set1_epi8(_value); } + static simdjson_inline simd8 zero() { return _mm256_setzero_si256(); } + static simdjson_inline simd8 load(const T values[32]) { + return _mm256_loadu_si256(reinterpret_cast(values)); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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( + 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() {} - simdjson_inline base8_numeric(const __m256i _value) : base8(_value) {} + simdjson_inline base8_numeric() : base8() {} + simdjson_inline base8_numeric(const __m256i _value) : base8(_value) {} - // Store to array - simdjson_inline void store(T dst[32]) const { return _mm256_storeu_si256(reinterpret_cast<__m256i*>(dst), *this); } + // 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 operator+(const simd8 other) const { return _mm256_add_epi8(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return _mm256_sub_epi8(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { return _mm256_add_epi8(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return _mm256_sub_epi8(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } - // Override to distinguish from bool version - simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return _mm256_shuffle_epi8(lookup_table, *this); - } + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 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 compress(uint32_t mask) would be - // sensible, but the AVX ISA makes this kind of approach difficult. - template - 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]; + // 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 compress(uint32_t mask) would be + // sensible, but the AVX ISA makes this kind of approach difficult. + template + 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(pshufb_combine_table + pop1 * 8))); - __m256i compactmask = _mm256_insertf128_si256(v256, - _mm_loadu_si128(reinterpret_cast(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); - } + // 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(pshufb_combine_table + pop1 * 8))); + __m256i compactmask = _mm256_insertf128_si256(v256, + _mm_loadu_si128(reinterpret_cast(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 - simdjson_inline simd8 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::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - }; + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; - // Signed bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( - 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 - ); - } + // Signed bytes + template<> + struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( + 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 max_val(const simd8 other) const { return _mm256_max_epi8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm256_min_epi8(*this, other); } - simdjson_inline simd8 operator>(const simd8 other) const { return _mm256_cmpgt_epi8(*this, other); } - simdjson_inline simd8 operator<(const simd8 other) const { return _mm256_cmpgt_epi8(other, *this); } - }; + // Order-sensitive comparisons + simdjson_inline simd8 max_val(const simd8 other) const { return _mm256_max_epi8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm256_min_epi8(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return _mm256_cmpgt_epi8(*this, other); } + simdjson_inline simd8 operator<(const simd8 other) const { return _mm256_cmpgt_epi8(other, *this); } + }; - // Unsigned bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( - 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 - ); - } + // Unsigned bytes + template<> + struct simd8: base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( + 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 saturating_add(const simd8 other) const { return _mm256_adds_epu8(*this, other); } - simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm256_subs_epu8(*this, other); } + // Saturated math + simdjson_inline simd8 saturating_add(const simd8 other) const { return _mm256_adds_epu8(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm256_subs_epu8(*this, other); } - // Order-specific operations - simdjson_inline simd8 max_val(const simd8 other) const { return _mm256_max_epu8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm256_min_epu8(other, *this); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } - simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } - simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } - simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } + // Order-specific operations + simdjson_inline simd8 max_val(const simd8 other) const { return _mm256_max_epu8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm256_min_epu8(other, *this); } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } + simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } + simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } + simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } - // Bit-specific operations - simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } - simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } - simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } - simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return _mm256_testz_si256(*this, bits); } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } - template - simdjson_inline simd8 shr() const { return simd8(_mm256_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } - template - simdjson_inline simd8 shl() const { return simd8(_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 - simdjson_inline int get_bit() const { return _mm256_movemask_epi8(_mm256_slli_epi16(*this, 7 - N)); } - }; + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } + simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } + simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } + simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return _mm256_testz_si256(*this, bits); } + simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } + template + simdjson_inline simd8 shr() const { return simd8(_mm256_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } + template + simdjson_inline simd8 shl() const { return simd8(_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 + simdjson_inline int get_bit() const { return _mm256_movemask_epi8(_mm256_slli_epi16(*this, 7-N)); } + }; - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 2, "Haswell kernel should use two registers per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 2, "Haswell kernel should use two registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1) : chunks{ chunk0, chunk1 } {} - simdjson_inline simd8x64(const T ptr[64]) : chunks{ simd8::load(ptr), simd8::load(ptr + 32) } {} + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1) : chunks{chunk0, chunk1} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::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 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) * 0); - this->chunks[1].store(ptr + sizeof(simd8) * 1); - } + simdjson_inline void store(T ptr[64]) const { + this->chunks[0].store(ptr+sizeof(simd8)*0); + this->chunks[1].store(ptr+sizeof(simd8)*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 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 reduce_or() const { - return this->chunks[0] | this->chunks[1]; - } + simdjson_inline simd8 reduce_or() const { + return this->chunks[0] | this->chunks[1]; + } - simdjson_inline simd8x64 bit_or(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] | mask, - this->chunks[1] | mask - ); - } + simdjson_inline simd8x64 bit_or(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] | mask, + this->chunks[1] | mask + ); + } - simdjson_inline uint64_t eq(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] == mask, - this->chunks[1] == mask - ).to_bitmask(); - } + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] == mask, + this->chunks[1] == mask + ).to_bitmask(); + } - simdjson_inline uint64_t eq(const simd8x64& other) const { - return simd8x64( - this->chunks[0] == other.chunks[0], - this->chunks[1] == other.chunks[1] - ).to_bitmask(); - } + simdjson_inline uint64_t eq(const simd8x64 &other) const { + return simd8x64( + 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] <= mask, - this->chunks[1] <= mask - ).to_bitmask(); - } - }; // struct simd8x64 + simdjson_inline uint64_t lteq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] <= mask, + this->chunks[1] <= mask + ).to_bitmask(); + } + }; // struct simd8x64 - } // namespace simd +} // namespace simd - } // unnamed namespace - } // namespace haswell +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_HASWELL_SIMD_H @@ -14817,41 +16150,66 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace { +namespace haswell { +namespace { - using namespace simd; +using namespace simd; - // Holds backslashes and quotes locations. - struct backslash_and_quote { - public: - static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t* src, uint8_t* dst); +// 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); } + 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 + 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 v(src); - // store to dest unconditionally - we can overwrite the bits we don't like later - v.store(dst); - return { - static_cast((v == '\\').to_bitmask()), // bs_bits - static_cast((v == '"').to_bitmask()), // quote_bits - }; - } +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 v(src); + // store to dest unconditionally - we can overwrite the bits we don't like later + v.store(dst); + return { + static_cast((v == '\\').to_bitmask()), // bs_bits + static_cast((v == '"').to_bitmask()), // quote_bits + }; +} - } // unnamed namespace - } // namespace haswell + +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 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 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 @@ -14884,10 +16242,12 @@ namespace simdjson { /* 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_LSX */ -/* amalgamation skipped (editor-only): #include "simdjson/lsx/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 */ @@ -14897,22 +16257,22 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { +namespace haswell { - struct open_container; - class dom_parser_implementation; +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 - }; +/** + * 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 haswell } // namespace simdjson #endif // SIMDJSON_GENERIC_BASE_H @@ -14929,99 +16289,97 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace { - namespace jsoncharutils { +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]; - } +// 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]; - } +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; - } +// 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 - } +// 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; - } +// 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 jsoncharutils +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_GENERIC_JSONCHARUTILS_H @@ -15039,69 +16397,69 @@ namespace simdjson { #include namespace simdjson { - namespace haswell { - namespace { - /// @private - namespace atomparsing { +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; } +// 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; } - // 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); - } +// 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); +} - 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) { + 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_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) { + 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_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) { + 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; } - } +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; } +} - } // namespace atomparsing - } // unnamed namespace - } // namespace haswell +} // namespace atomparsing +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_GENERIC_ATOMPARSING_H @@ -15117,83 +16475,83 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { +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 +// 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"); +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_containers{}; - /** Whether each open container is a [ or { */ - std::unique_ptr 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{}; +class dom_parser_implementation final : public internal::dom_parser_implementation { +public: + /** Tape location of each open { or [ */ + std::unique_ptr open_containers{}; + /** Whether each open container is a [ or { */ + std::unique_ptr 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; + 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); + 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 haswell } // namespace simdjson namespace simdjson { - namespace haswell { +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; +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) { return CAPACITY; } - // Stage 1 index output - size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; - 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; +// 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) { return CAPACITY; } + // Stage 1 index output + size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; + 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; - } + _capacity = capacity; + return SUCCESS; +} - inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { - // 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; } +inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { + // 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; - } + _max_depth = max_depth; + return SUCCESS; +} - } // namespace haswell +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H @@ -15208,129 +16566,148 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { +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 : public internal::implementation_simdjson_result_base { - * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} - * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} - * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} - * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} - * // Your extra methods here - * } - * - * Then any method returning simdjson_result will be chainable with your methods. - */ - template - struct implementation_simdjson_result_base { +// 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 : public internal::implementation_simdjson_result_base { + * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} + * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} + * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} + * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} + * // Your extra methods here + * } + * + * Then any method returning simdjson_result will be chainable with your methods. + */ +template +struct implementation_simdjson_result_base { - /** - * Create a new empty result with error = UNINITIALIZED. - */ - simdjson_inline implementation_simdjson_result_base() noexcept = default; + /** + * 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 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 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; + /** + * 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 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; + /** + * 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_inline error_code error() const 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& value() & noexcept(false); + /** + * 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); - /** - * Take the result value (move it). - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline T&& value() && 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&& take_value() && noexcept(false); + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& 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); + /** + * 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; - 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 + /** + * 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; - } // namespace haswell + 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 @@ -15351,8 +16728,8 @@ namespace simdjson { #include namespace simdjson { - namespace haswell { - namespace numberparsing { +namespace haswell { +namespace numberparsing { #ifdef JSON_TEST_NUMBERS #define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) @@ -15368,540 +16745,547 @@ namespace simdjson { #define BIGINT_NUMBER(SRC) (BIGINT_ERROR) #endif - namespace { +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(negative)) << 63); - std::memcpy(&d, &mantissa, sizeof(d)); - return d; - } +// 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(negative)) << 63); + std::memcpy(&d, &mantissa, sizeof(d)); + return d; +} - // 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 +// 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) + if (0 <= power && power <= 22 && i <= 9007199254740991) #else - if (-22 <= power && power <= 22 && i <= 9007199254740991) + 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 + { + // 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. + // 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; - } + // 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 - // approximatively 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; + // 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 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. - simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]); - // 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. + // 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 - // 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. - simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); - 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); + // 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. - // 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 - } - } + // 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); - mantissa += mantissa & 1; - mantissa >>= 1; + // 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 + } + } - // 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; - } + mantissa += mantissa & 1; + mantissa >>= 1; - // 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(ptr)); - // We do not accept infinite values. + // 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; +} - // 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::max)() || *outDouble < std::numeric_limits::lowest()); - } +// 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(ptr)); + // We do not accept infinite values. - static bool parse_float_fallback(const uint8_t* ptr, const uint8_t* end_ptr, double* outDouble) { - *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(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::max)() || *outDouble < std::numeric_limits::lowest()); +} - // 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::max)() || *outDouble < std::numeric_limits::lowest()); - } +static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) { + *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(end_ptr)); + // We do not accept infinite values. - // 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); - } + // 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::max)() || *outDouble < std::numeric_limits::lowest()); +} - template - 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(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; - } +// 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); +} - simdjson_inline bool is_digit(const uint8_t c) { - return static_cast(c - '0') <= 9; - } +template +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(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 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; +simdjson_inline bool is_digit(const uint8_t c) { + return static_cast(c - '0') <= 9; +} + +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; #ifdef SIMDJSON_SWAR_NUMBER_PARSING #if SIMDJSON_SWAR_NUMBER_PARSING - // this helps if we have lots of decimals! - // this turns out to be frequent enough. - if (is_made_of_eight_digits_fast(p)) { - i = i * 100000000 + parse_eight_digits_unrolled(p); - p += 8; - } + // this helps if we have lots of decimals! + // this turns out to be frequent enough. + if (is_made_of_eight_digits_fast(p)) { + i = i * 100000000 + parse_eight_digits_unrolled(p); + p += 8; + } #endif // SIMDJSON_SWAR_NUMBER_PARSING #endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING - // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) - if (parse_digit(*p, i)) { ++p; } - while (parse_digit(*p, i)) { p++; } - exponent = first_after_period - p; - // Decimal without digits (123.) is illegal - if (exponent == 0) { - return INVALID_NUMBER(src); - } - return SUCCESS; - } + // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) + if (parse_digit(*p, i)) { ++p; } + while (parse_digit(*p, i)) { p++; } + exponent = first_after_period - p; + // Decimal without digits (123.) is illegal + if (exponent == 0) { + return INVALID_NUMBER(src); + } + return SUCCESS; +} - 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 +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. + // 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. + // 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 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; - } + // 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 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); - } +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 +} // unnamed namespace - /** @private */ - static 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 */ +static 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 - 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; - } +/** @private */ +template +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 - simdjson_inline error_code parse_number(const uint8_t* const src, W& writer); +// 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 +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 +// for performance analysis, it is sometimes useful to skip parsing #ifdef SIMDJSON_SKIPNUMBERPARSING - template - simdjson_inline error_code parse_number(const uint8_t* const, W& writer) { - writer.append_s64(0); // always write zero - return SUCCESS; // always succeeds - } +template +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 parse_unsigned(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline bool is_negative(const uint8_t* src) noexcept { return false; } - simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t* src) noexcept { return false; } - simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t* src) noexcept { return number_type::signed_integer; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t * src) noexcept { return number_type::signed_integer; } #else // parse the number at src @@ -15913,744 +17297,753 @@ namespace simdjson { // content and append a space before calling this function. // // Our objective is accurate parsing (ULP of 0) at high speed. - template - 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)) { 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 INVALID_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 { - WRITE_INTEGER(negative ? (~i + 1) : i, src, writer); - } - 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 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; - 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); - // 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 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 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; - 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); - // 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 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; - 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); - // 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 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 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; - while (parse_digit(*src, i)) { src++; } - - // 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 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; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (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 == '.')) { - p++; - const uint8_t* start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (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 = 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; - } - - simdjson_unused simdjson_inline bool is_negative(const uint8_t* src) noexcept { - return (*src == '-'); - } - - simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t* src) noexcept { - bool negative = (*src == '-'); - src += uint8_t(negative); - const uint8_t* p = src; - while (static_cast(*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 get_number_type(const uint8_t* src) noexcept { - bool negative = (*src == '-'); - src += uint8_t(negative); - const uint8_t* p = src; - while (static_cast(*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("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; - } - 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("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 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 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; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (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 == '.')) { - p++; - const uint8_t* start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (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 = 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; - } - - } // unnamed namespace +template +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)) { 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 INVALID_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 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; + 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); + // 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 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 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; + 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); + // 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 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; + 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); + // 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 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 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; + while (parse_digit(*src, i)) { src++; } + + // 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { + return (*src == '-'); +} + +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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 get_number_type(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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("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("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 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +} // unnamed namespace #endif // SIMDJSON_SKIPNUMBERPARSING - } // namespace numberparsing +} // 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; - } +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 haswell } // namespace simdjson #endif // SIMDJSON_GENERIC_NUMBERPARSING_H @@ -16667,84 +18060,113 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { +namespace haswell { - // - // internal::implementation_simdjson_result_base inline implementation - // +// +// internal::implementation_simdjson_result_base inline implementation +// - template - simdjson_inline void implementation_simdjson_result_base::tie(T& value, error_code& error) && noexcept { - error = this->second; - if (!error) { - value = std::forward>(*this).first; - } - } +template +simdjson_inline void implementation_simdjson_result_base::tie(T &value, error_code &error) && noexcept { + error = this->second; + if (!error) { + value = std::forward>(*this).first; + } +} - template - simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T& value) && noexcept { - error_code error; - std::forward>(*this).tie(value, error); - return error; - } +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T &value) && noexcept { + error_code error; + std::forward>(*this).tie(value, error); + return error; +} - template - simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { - return this->second; - } +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { + return this->second; +} + + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} #if SIMDJSON_EXCEPTIONS - template - simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return this->first; - } +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} - template - simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { - return std::forward>(*this).take_value(); - } +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} - template - simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return std::forward(this->first); - } +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} - template - simdjson_inline implementation_simdjson_result_base::operator T && () && noexcept(false) { - return std::forward>(*this).take_value(); - } + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + +template +simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return this->first; +} + +template +simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { + return std::forward>(*this).take_value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return std::forward(this->first); +} + +template +simdjson_inline implementation_simdjson_result_base::operator T&&() && noexcept(false) { + return std::forward>(*this).take_value(); +} #endif // SIMDJSON_EXCEPTIONS - template - simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { - return this->first; - } +template +simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { + return this->first; +} - template - simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { - return this->first; - } +template +simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { + return this->first; +} - template - simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { - return std::forward(this->first); - } +template +simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { + return std::forward(this->first); +} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T&& value, error_code error) noexcept - : first{ std::forward(value) }, second{ error } {} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept - : implementation_simdjson_result_base(T{}, error) {} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T&& value) noexcept - : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value, error_code error) noexcept + : first{std::forward(value)}, second{error} {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept + : implementation_simdjson_result_base(T{}, error) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value) noexcept + : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} - } // namespace haswell +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H @@ -16779,28 +18201,28 @@ SIMDJSON_UNTARGET_REGION // The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_HASWELL namespace simdjson { - namespace haswell { +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& 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; - }; +/** + * @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& 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 haswell } // namespace simdjson #endif // SIMDJSON_HASWELL_IMPLEMENTATION_H @@ -16822,21 +18244,21 @@ namespace simdjson { // 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 { +/** + * Implementation for Haswell (Intel AVX2). + */ +namespace haswell { - class implementation; +class implementation; - namespace { - namespace simd { - template struct simd8; - template struct simd8x64; - } // namespace simd - } // unnamed namespace +namespace { +namespace simd { +template struct simd8; +template struct simd8x64; +} // namespace simd +} // unnamed namespace - } // namespace haswell +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_HASWELL_BASE_H @@ -16884,8 +18306,8 @@ namespace simdjson { #include #include #include // for _mm_clmulepi64_si128 - // unfortunately, we may not get _blsr_u64, but, thankfully, clang - // has it as a macro. +// 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) @@ -16898,8 +18320,14 @@ static_assert(sizeof(__m256i) <= simdjson::SIMDJSON_PADDING, "insufficient paddi /* 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 */ @@ -16913,64 +18341,64 @@ SIMDJSON_TARGET_REGION("avx2,bmi,pclmul,lzcnt,popcnt") /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace { +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) { +// 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); + 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); + //////// + // 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 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)); - } +/* 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 - } +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); - } +simdjson_inline long long int count_ones(uint64_t input_num) { + return _popcnt64(input_num); +} #endif - simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, - uint64_t* result) { +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { #if SIMDJSON_REGULAR_VISUAL_STUDIO - return _addcarry_u64(0, value1, value2, - reinterpret_cast(result)); + return _addcarry_u64(0, value1, value2, + reinterpret_cast(result)); #else - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); #endif - } +} - } // unnamed namespace - } // namespace haswell +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_HASWELL_BITMANIPULATION_H @@ -16986,24 +18414,24 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace { +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); - } +// +// 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 +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_HASWELL_BITMASK_H @@ -17023,49 +18451,49 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace numberparsing { +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(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 */ +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(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; +/** @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; + // 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 + 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); + __uint128_t r = (static_cast<__uint128_t>(value1)) * value2; + answer.low = uint64_t(r); + answer.high = uint64_t(r >> 64); #endif - return answer; - } + return answer; +} - } // namespace numberparsing - } // namespace haswell +} // namespace numberparsing +} // namespace haswell } // namespace simdjson #define SIMDJSON_SWAR_NUMBER_PARSING 1 @@ -17085,364 +18513,365 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace { - namespace simd { +namespace haswell { +namespace { +namespace simd { - // Forward-declared so they can be used by splat and friends. - template - struct base { - __m256i value; + // Forward-declared so they can be used by splat and friends. + template + struct base { + __m256i value; - // Zero constructor - simdjson_inline base() : value{ __m256i() } {} + // Zero constructor + simdjson_inline base() : value{__m256i()} {} - // Conversion from SIMD register - simdjson_inline base(const __m256i _value) : value(_value) {} + // 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; } + // 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(this); *this_cast = *this_cast | other; return *this_cast; } - simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } - simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } - }; + // 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(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; - // Forward-declared so they can be used by splat and friends. - template - struct simd8; + // Forward-declared so they can be used by splat and friends. + template + struct simd8; - template> - struct base8 : base> { - typedef uint32_t bitmask_t; - typedef uint64_t bitmask2_t; + template> + struct base8: base> { + typedef uint32_t bitmask_t; + typedef uint64_t bitmask2_t; - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m256i _value) : base>(_value) {} + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m256i _value) : base>(_value) {} - friend simdjson_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm256_cmpeq_epi8(lhs, rhs); } + friend simdjson_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm256_cmpeq_epi8(lhs, rhs); } - static const int SIZE = sizeof(base::value); + static const int SIZE = sizeof(base::value); - template - simdjson_inline simd8 prev(const simd8 prev_chunk) const { - return _mm256_alignr_epi8(*this, _mm256_permute2x128_si256(prev_chunk, *this, 0x21), 16 - N); - } - }; + template + simdjson_inline simd8 prev(const simd8 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 : base8 { - static simdjson_inline simd8 splat(bool _value) { return _mm256_set1_epi8(uint8_t(-(!!_value))); } + // SIMD byte mask type (returned by things like eq and gt) + template<> + struct simd8: base8 { + static simdjson_inline simd8 splat(bool _value) { return _mm256_set1_epi8(uint8_t(-(!!_value))); } - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m256i _value) : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) : base8(splat(_value)) {} + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m256i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(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 operator~() const { return *this ^ true; } - }; + 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 operator~() const { return *this ^ true; } + }; - template - struct base8_numeric : base8 { - static simdjson_inline simd8 splat(T _value) { return _mm256_set1_epi8(_value); } - static simdjson_inline simd8 zero() { return _mm256_setzero_si256(); } - static simdjson_inline simd8 load(const T values[32]) { - return _mm256_loadu_si256(reinterpret_cast(values)); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdjson_inline simd8 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( - 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 - ); - } + template + struct base8_numeric: base8 { + static simdjson_inline simd8 splat(T _value) { return _mm256_set1_epi8(_value); } + static simdjson_inline simd8 zero() { return _mm256_setzero_si256(); } + static simdjson_inline simd8 load(const T values[32]) { + return _mm256_loadu_si256(reinterpret_cast(values)); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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( + 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() {} - simdjson_inline base8_numeric(const __m256i _value) : base8(_value) {} + simdjson_inline base8_numeric() : base8() {} + simdjson_inline base8_numeric(const __m256i _value) : base8(_value) {} - // Store to array - simdjson_inline void store(T dst[32]) const { return _mm256_storeu_si256(reinterpret_cast<__m256i*>(dst), *this); } + // 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 operator+(const simd8 other) const { return _mm256_add_epi8(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return _mm256_sub_epi8(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { return _mm256_add_epi8(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return _mm256_sub_epi8(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } - // Override to distinguish from bool version - simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return _mm256_shuffle_epi8(lookup_table, *this); - } + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 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 compress(uint32_t mask) would be - // sensible, but the AVX ISA makes this kind of approach difficult. - template - 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]; + // 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 compress(uint32_t mask) would be + // sensible, but the AVX ISA makes this kind of approach difficult. + template + 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(pshufb_combine_table + pop1 * 8))); - __m256i compactmask = _mm256_insertf128_si256(v256, - _mm_loadu_si128(reinterpret_cast(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); - } + // 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(pshufb_combine_table + pop1 * 8))); + __m256i compactmask = _mm256_insertf128_si256(v256, + _mm_loadu_si128(reinterpret_cast(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 - simdjson_inline simd8 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::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - }; + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; - // Signed bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( - 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 - ); - } + // Signed bytes + template<> + struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( + 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 max_val(const simd8 other) const { return _mm256_max_epi8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm256_min_epi8(*this, other); } - simdjson_inline simd8 operator>(const simd8 other) const { return _mm256_cmpgt_epi8(*this, other); } - simdjson_inline simd8 operator<(const simd8 other) const { return _mm256_cmpgt_epi8(other, *this); } - }; + // Order-sensitive comparisons + simdjson_inline simd8 max_val(const simd8 other) const { return _mm256_max_epi8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm256_min_epi8(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return _mm256_cmpgt_epi8(*this, other); } + simdjson_inline simd8 operator<(const simd8 other) const { return _mm256_cmpgt_epi8(other, *this); } + }; - // Unsigned bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( - 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 - ); - } + // Unsigned bytes + template<> + struct simd8: base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( + 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 saturating_add(const simd8 other) const { return _mm256_adds_epu8(*this, other); } - simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm256_subs_epu8(*this, other); } + // Saturated math + simdjson_inline simd8 saturating_add(const simd8 other) const { return _mm256_adds_epu8(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm256_subs_epu8(*this, other); } - // Order-specific operations - simdjson_inline simd8 max_val(const simd8 other) const { return _mm256_max_epu8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm256_min_epu8(other, *this); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } - simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } - simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } - simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } + // Order-specific operations + simdjson_inline simd8 max_val(const simd8 other) const { return _mm256_max_epu8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm256_min_epu8(other, *this); } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } + simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } + simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } + simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } - // Bit-specific operations - simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } - simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } - simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } - simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return _mm256_testz_si256(*this, bits); } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } - template - simdjson_inline simd8 shr() const { return simd8(_mm256_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } - template - simdjson_inline simd8 shl() const { return simd8(_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 - simdjson_inline int get_bit() const { return _mm256_movemask_epi8(_mm256_slli_epi16(*this, 7 - N)); } - }; + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } + simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } + simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } + simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return _mm256_testz_si256(*this, bits); } + simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } + template + simdjson_inline simd8 shr() const { return simd8(_mm256_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } + template + simdjson_inline simd8 shl() const { return simd8(_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 + simdjson_inline int get_bit() const { return _mm256_movemask_epi8(_mm256_slli_epi16(*this, 7-N)); } + }; - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 2, "Haswell kernel should use two registers per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 2, "Haswell kernel should use two registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1) : chunks{ chunk0, chunk1 } {} - simdjson_inline simd8x64(const T ptr[64]) : chunks{ simd8::load(ptr), simd8::load(ptr + 32) } {} + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1) : chunks{chunk0, chunk1} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::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 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) * 0); - this->chunks[1].store(ptr + sizeof(simd8) * 1); - } + simdjson_inline void store(T ptr[64]) const { + this->chunks[0].store(ptr+sizeof(simd8)*0); + this->chunks[1].store(ptr+sizeof(simd8)*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 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 reduce_or() const { - return this->chunks[0] | this->chunks[1]; - } + simdjson_inline simd8 reduce_or() const { + return this->chunks[0] | this->chunks[1]; + } - simdjson_inline simd8x64 bit_or(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] | mask, - this->chunks[1] | mask - ); - } + simdjson_inline simd8x64 bit_or(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] | mask, + this->chunks[1] | mask + ); + } - simdjson_inline uint64_t eq(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] == mask, - this->chunks[1] == mask - ).to_bitmask(); - } + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] == mask, + this->chunks[1] == mask + ).to_bitmask(); + } - simdjson_inline uint64_t eq(const simd8x64& other) const { - return simd8x64( - this->chunks[0] == other.chunks[0], - this->chunks[1] == other.chunks[1] - ).to_bitmask(); - } + simdjson_inline uint64_t eq(const simd8x64 &other) const { + return simd8x64( + 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] <= mask, - this->chunks[1] <= mask - ).to_bitmask(); - } - }; // struct simd8x64 + simdjson_inline uint64_t lteq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] <= mask, + this->chunks[1] <= mask + ).to_bitmask(); + } + }; // struct simd8x64 - } // namespace simd +} // namespace simd - } // unnamed namespace - } // namespace haswell +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_HASWELL_SIMD_H @@ -17459,41 +18888,66 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace { +namespace haswell { +namespace { - using namespace simd; +using namespace simd; - // Holds backslashes and quotes locations. - struct backslash_and_quote { - public: - static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t* src, uint8_t* dst); +// 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); } + 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 + 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 v(src); - // store to dest unconditionally - we can overwrite the bits we don't like later - v.store(dst); - return { - static_cast((v == '\\').to_bitmask()), // bs_bits - static_cast((v == '"').to_bitmask()), // quote_bits - }; - } +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 v(src); + // store to dest unconditionally - we can overwrite the bits we don't like later + v.store(dst); + return { + static_cast((v == '\\').to_bitmask()), // bs_bits + static_cast((v == '"').to_bitmask()), // quote_bits + }; +} - } // unnamed namespace - } // namespace haswell + +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 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 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 @@ -17516,13 +18970,13 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace { +namespace haswell { +namespace { - struct json_character_block; +struct json_character_block; - } // unnamed namespace - } // namespace haswell +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_BASE_H @@ -17539,14 +18993,14 @@ namespace simdjson { // Interface a dom parser implementation must fulfill namespace simdjson { - namespace haswell { - namespace { +namespace haswell { +namespace { - simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3); - simdjson_inline bool is_ascii(const simd8x64& input); +simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3); +simdjson_inline bool is_ascii(const simd8x64& input); - } // unnamed namespace - } // namespace haswell +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_DOM_PARSER_IMPLEMENTATION_H @@ -17561,22 +19015,22 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace { +namespace haswell { +namespace { - struct json_character_block { - static simdjson_inline json_character_block classify(const simd::simd8x64& in); +struct json_character_block { + static simdjson_inline json_character_block classify(const simd::simd8x64& in); - simdjson_inline uint64_t whitespace() const noexcept { return _whitespace; } - simdjson_inline uint64_t op() const noexcept { return _op; } - simdjson_inline uint64_t scalar() const noexcept { return ~(op() | whitespace()); } + simdjson_inline uint64_t whitespace() const noexcept { return _whitespace; } + simdjson_inline uint64_t op() const noexcept { return _op; } + simdjson_inline uint64_t scalar() const noexcept { return ~(op() | whitespace()); } - uint64_t _whitespace; - uint64_t _op; - }; + uint64_t _whitespace; + uint64_t _op; +}; - } // unnamed namespace - } // namespace haswell +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_JSON_CHARACTER_BLOCK_H @@ -17595,30 +19049,30 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace { - namespace stage1 { +namespace haswell { +namespace { +namespace stage1 { - class bit_indexer; - template - struct buf_block_reader; - struct json_block; - class json_minifier; - class json_scanner; - struct json_string_block; - class json_string_scanner; - class json_structural_indexer; +class bit_indexer; +template +struct buf_block_reader; +struct json_block; +class json_minifier; +class json_scanner; +struct json_string_block; +class json_string_scanner; +class json_structural_indexer; - } // namespace stage1 +} // namespace stage1 - namespace utf8_validation { - struct utf8_checker; - } // namespace utf8_validation +namespace utf8_validation { +struct utf8_checker; +} // namespace utf8_validation - using utf8_validation::utf8_checker; +using utf8_validation::utf8_checker; - } // unnamed namespace - } // namespace haswell +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_BASE_H @@ -17635,109 +19089,109 @@ namespace simdjson { #include namespace simdjson { - namespace haswell { - namespace { - namespace stage1 { +namespace haswell { +namespace { +namespace stage1 { - // Walks through a buffer in block-sized increments, loading the last part with spaces - template - struct buf_block_reader { - public: - simdjson_inline buf_block_reader(const uint8_t* _buf, size_t _len); - simdjson_inline size_t block_index(); - simdjson_inline bool has_full_block() const; - simdjson_inline const uint8_t* full_block() const; - /** - * Get the last block, padded with spaces. - * - * There will always be a last block, with at least 1 byte, unless len == 0 (in which case this - * function fills the buffer with spaces and returns 0. In particular, if len == STEP_SIZE there - * will be 0 full_blocks and 1 remainder block with STEP_SIZE bytes and no spaces for padding. - * - * @return the number of effective characters in the last block. - */ - simdjson_inline size_t get_remainder(uint8_t* dst) const; - simdjson_inline void advance(); - private: - const uint8_t* buf; - const size_t len; - const size_t lenminusstep; - size_t idx; - }; +// Walks through a buffer in block-sized increments, loading the last part with spaces +template +struct buf_block_reader { +public: + simdjson_inline buf_block_reader(const uint8_t *_buf, size_t _len); + simdjson_inline size_t block_index(); + simdjson_inline bool has_full_block() const; + simdjson_inline const uint8_t *full_block() const; + /** + * Get the last block, padded with spaces. + * + * There will always be a last block, with at least 1 byte, unless len == 0 (in which case this + * function fills the buffer with spaces and returns 0. In particular, if len == STEP_SIZE there + * will be 0 full_blocks and 1 remainder block with STEP_SIZE bytes and no spaces for padding. + * + * @return the number of effective characters in the last block. + */ + simdjson_inline size_t get_remainder(uint8_t *dst) const; + simdjson_inline void advance(); +private: + const uint8_t *buf; + const size_t len; + const size_t lenminusstep; + size_t idx; +}; - // Routines to print masks and text for debugging bitmask operations - simdjson_unused static char* format_input_text_64(const uint8_t* text) { - static char buf[sizeof(simd8x64) + 1]; - for (size_t i = 0; i < sizeof(simd8x64); i++) { - buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]); - } - buf[sizeof(simd8x64)] = '\0'; - return buf; - } +// Routines to print masks and text for debugging bitmask operations +simdjson_unused static char * format_input_text_64(const uint8_t *text) { + static char buf[sizeof(simd8x64) + 1]; + for (size_t i=0; i); i++) { + buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]); + } + buf[sizeof(simd8x64)] = '\0'; + return buf; +} - // Routines to print masks and text for debugging bitmask operations - simdjson_unused static char* format_input_text(const simd8x64& in) { - static char buf[sizeof(simd8x64) + 1]; - in.store(reinterpret_cast(buf)); - for (size_t i = 0; i < sizeof(simd8x64); i++) { - if (buf[i] < ' ') { buf[i] = '_'; } - } - buf[sizeof(simd8x64)] = '\0'; - return buf; - } +// Routines to print masks and text for debugging bitmask operations +simdjson_unused static char * format_input_text(const simd8x64& in) { + static char buf[sizeof(simd8x64) + 1]; + in.store(reinterpret_cast(buf)); + for (size_t i=0; i); i++) { + if (buf[i] < ' ') { buf[i] = '_'; } + } + buf[sizeof(simd8x64)] = '\0'; + return buf; +} - simdjson_unused static char* format_input_text(const simd8x64& in, uint64_t mask) { - static char buf[sizeof(simd8x64) + 1]; - in.store(reinterpret_cast(buf)); - for (size_t i = 0; i < sizeof(simd8x64); i++) { - if (buf[i] <= ' ') { buf[i] = '_'; } - if (!(mask & (size_t(1) << i))) { buf[i] = ' '; } - } - buf[sizeof(simd8x64)] = '\0'; - return buf; - } +simdjson_unused static char * format_input_text(const simd8x64& in, uint64_t mask) { + static char buf[sizeof(simd8x64) + 1]; + in.store(reinterpret_cast(buf)); + for (size_t i=0; i); i++) { + if (buf[i] <= ' ') { buf[i] = '_'; } + if (!(mask & (size_t(1) << i))) { buf[i] = ' '; } + } + buf[sizeof(simd8x64)] = '\0'; + return buf; +} - simdjson_unused static char* format_mask(uint64_t mask) { - static char buf[sizeof(simd8x64) + 1]; - for (size_t i = 0; i < 64; i++) { - buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' '; - } - buf[64] = '\0'; - return buf; - } +simdjson_unused static char * format_mask(uint64_t mask) { + static char buf[sizeof(simd8x64) + 1]; + for (size_t i=0; i<64; i++) { + buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' '; + } + buf[64] = '\0'; + return buf; +} - template - simdjson_inline buf_block_reader::buf_block_reader(const uint8_t* _buf, size_t _len) : buf{ _buf }, len{ _len }, lenminusstep{ len < STEP_SIZE ? 0 : len - STEP_SIZE }, idx{ 0 } {} +template +simdjson_inline buf_block_reader::buf_block_reader(const uint8_t *_buf, size_t _len) : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE}, idx{0} {} - template - simdjson_inline size_t buf_block_reader::block_index() { return idx; } +template +simdjson_inline size_t buf_block_reader::block_index() { return idx; } - template - simdjson_inline bool buf_block_reader::has_full_block() const { - return idx < lenminusstep; - } +template +simdjson_inline bool buf_block_reader::has_full_block() const { + return idx < lenminusstep; +} - template - simdjson_inline const uint8_t* buf_block_reader::full_block() const { - return &buf[idx]; - } +template +simdjson_inline const uint8_t *buf_block_reader::full_block() const { + return &buf[idx]; +} - template - simdjson_inline size_t buf_block_reader::get_remainder(uint8_t* dst) const { - if (len == idx) { return 0; } // memcpy(dst, null, 0) will trigger an error with some sanitizers - std::memset(dst, 0x20, STEP_SIZE); // std::memset STEP_SIZE because it's more efficient to write out 8 or 16 bytes at once. - std::memcpy(dst, buf + idx, len - idx); - return len - idx; - } +template +simdjson_inline size_t buf_block_reader::get_remainder(uint8_t *dst) const { + if(len == idx) { return 0; } // memcpy(dst, null, 0) will trigger an error with some sanitizers + std::memset(dst, 0x20, STEP_SIZE); // std::memset STEP_SIZE because it's more efficient to write out 8 or 16 bytes at once. + std::memcpy(dst, buf + idx, len - idx); + return len - idx; +} - template - simdjson_inline void buf_block_reader::advance() { - idx += STEP_SIZE; - } +template +simdjson_inline void buf_block_reader::advance() { + idx += STEP_SIZE; +} - } // namespace stage1 - } // unnamed namespace - } // namespace haswell +} // namespace stage1 +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_BUF_BLOCK_READER_H @@ -17753,145 +19207,145 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace { - namespace stage1 { +namespace haswell { +namespace { +namespace stage1 { - /** - * Scans for escape characters in JSON, taking care with multiple backslashes (\\n vs. \n). - */ - struct json_escape_scanner { - /** The actual escape characters (the backslashes themselves). */ - uint64_t next_is_escaped = 0ULL; +/** + * Scans for escape characters in JSON, taking care with multiple backslashes (\\n vs. \n). + */ +struct json_escape_scanner { + /** The actual escape characters (the backslashes themselves). */ + uint64_t next_is_escaped = 0ULL; - struct escaped_and_escape { - /** - * Mask of escaped characters. - * - * ``` - * \n \\n \\\n \\\\n \ - * 0100100010100101000 - * n \ \ n \ \ - * ``` - */ - uint64_t escaped; - /** - * Mask of escape characters. - * - * ``` - * \n \\n \\\n \\\\n \ - * 1001000101001010001 - * \ \ \ \ \ \ \ - * ``` - */ - uint64_t escape; - }; + struct escaped_and_escape { + /** + * Mask of escaped characters. + * + * ``` + * \n \\n \\\n \\\\n \ + * 0100100010100101000 + * n \ \ n \ \ + * ``` + */ + uint64_t escaped; + /** + * Mask of escape characters. + * + * ``` + * \n \\n \\\n \\\\n \ + * 1001000101001010001 + * \ \ \ \ \ \ \ + * ``` + */ + uint64_t escape; + }; - /** - * Get a mask of both escape and escaped characters (the characters following a backslash). - * - * @param potential_escape A mask of the character that can escape others (but could be - * escaped itself). e.g. block.eq('\\') - */ - simdjson_really_inline escaped_and_escape next(uint64_t backslash) noexcept { + /** + * Get a mask of both escape and escaped characters (the characters following a backslash). + * + * @param potential_escape A mask of the character that can escape others (but could be + * escaped itself). e.g. block.eq('\\') + */ + simdjson_really_inline escaped_and_escape next(uint64_t backslash) noexcept { #if !SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT - if (!backslash) { return { next_escaped_without_backslashes(), 0 }; } + if (!backslash) { return {next_escaped_without_backslashes(), 0}; } #endif - // | | Mask (shows characters instead of 1's) | Depth | Instructions | - // |--------------------------------|----------------------------------------|-------|---------------------| - // | string | `\\n_\\\n___\\\n___\\\\___\\\\__\\\` | | | - // | | ` even odd even odd odd` | | | - // | potential_escape | ` \ \\\ \\\ \\\\ \\\\ \\\` | 1 | 1 (backslash & ~first_is_escaped) - // | escape_and_terminal_code | ` \n \ \n \ \n \ \ \ \ \ \` | 5 | 5 (next_escape_and_terminal_code()) - // | escaped | `\ \ n \ n \ \ \ \ \ ` X | 6 | 7 (escape_and_terminal_code ^ (potential_escape | first_is_escaped)) - // | escape | ` \ \ \ \ \ \ \ \ \ \` | 6 | 8 (escape_and_terminal_code & backslash) - // | first_is_escaped | `\ ` | 7 (*) | 9 (escape >> 63) () - // (*) this is not needed until the next iteration - uint64_t escape_and_terminal_code = next_escape_and_terminal_code(backslash & ~this->next_is_escaped); - uint64_t escaped = escape_and_terminal_code ^ (backslash | this->next_is_escaped); - uint64_t escape = escape_and_terminal_code & backslash; - this->next_is_escaped = escape >> 63; - return { escaped, escape }; - } + // | | Mask (shows characters instead of 1's) | Depth | Instructions | + // |--------------------------------|----------------------------------------|-------|---------------------| + // | string | `\\n_\\\n___\\\n___\\\\___\\\\__\\\` | | | + // | | ` even odd even odd odd` | | | + // | potential_escape | ` \ \\\ \\\ \\\\ \\\\ \\\` | 1 | 1 (backslash & ~first_is_escaped) + // | escape_and_terminal_code | ` \n \ \n \ \n \ \ \ \ \ \` | 5 | 5 (next_escape_and_terminal_code()) + // | escaped | `\ \ n \ n \ \ \ \ \ ` X | 6 | 7 (escape_and_terminal_code ^ (potential_escape | first_is_escaped)) + // | escape | ` \ \ \ \ \ \ \ \ \ \` | 6 | 8 (escape_and_terminal_code & backslash) + // | first_is_escaped | `\ ` | 7 (*) | 9 (escape >> 63) () + // (*) this is not needed until the next iteration + uint64_t escape_and_terminal_code = next_escape_and_terminal_code(backslash & ~this->next_is_escaped); + uint64_t escaped = escape_and_terminal_code ^ (backslash | this->next_is_escaped); + uint64_t escape = escape_and_terminal_code & backslash; + this->next_is_escaped = escape >> 63; + return {escaped, escape}; + } - private: - static constexpr const uint64_t ODD_BITS = 0xAAAAAAAAAAAAAAAAULL; +private: + static constexpr const uint64_t ODD_BITS = 0xAAAAAAAAAAAAAAAAULL; - simdjson_really_inline uint64_t next_escaped_without_backslashes() noexcept { - uint64_t escaped = this->next_is_escaped; - this->next_is_escaped = 0; - return escaped; - } + simdjson_really_inline uint64_t next_escaped_without_backslashes() noexcept { + uint64_t escaped = this->next_is_escaped; + this->next_is_escaped = 0; + return escaped; + } - /** - * Returns a mask of the next escape characters (masking out escaped backslashes), along with - * any non-backslash escape codes. - * - * \n \\n \\\n \\\\n returns: - * \n \ \ \n \ \ - * 11 100 1011 10100 - * - * You are expected to mask out the first bit yourself if the previous block had a trailing - * escape. - * - * & the result with potential_escape to get just the escape characters. - * ^ the result with (potential_escape | first_is_escaped) to get escaped characters. - */ - static simdjson_really_inline uint64_t next_escape_and_terminal_code(uint64_t potential_escape) noexcept { - // If we were to just shift and mask out any odd bits, we'd actually get a *half* right answer: - // any even-aligned backslash runs would be correct! Odd-aligned backslash runs would be - // inverted (\\\ would be 010 instead of 101). - // - // ``` - // string: | ____\\\\_\\\\_____ | - // maybe_escaped | ODD | \ \ \ \ | - // even-aligned ^^^ ^^^^ odd-aligned - // ``` - // - // Taking that into account, our basic strategy is: - // - // 1. Use subtraction to produce a mask with 1's for even-aligned runs and 0's for - // odd-aligned runs. - // 2. XOR all odd bits, which masks out the odd bits in even-aligned runs, and brings IN the - // odd bits in odd-aligned runs. - // 3. & with backslash to clean up any stray bits. - // runs are set to 0, and then XORing with "odd": - // - // | | Mask (shows characters instead of 1's) | Instructions | - // |--------------------------------|----------------------------------------|---------------------| - // | string | `\\n_\\\n___\\\n___\\\\___\\\\__\\\` | - // | | ` even odd even odd odd` | - // | maybe_escaped | ` n \\n \\n \\\_ \\\_ \\` X | 1 (potential_escape << 1) - // | maybe_escaped_and_odd | ` \n_ \\n _ \\\n_ _ \\\__ _\\\_ \\\` | 1 (maybe_escaped | odd) - // | even_series_codes_and_odd | ` n_\\\ _ n_ _\\\\ _ _ ` | 1 (maybe_escaped_and_odd - potential_escape) - // | escape_and_terminal_code | ` \n \ \n \ \n \ \ \ \ \ \` | 1 (^ odd) - // + /** + * Returns a mask of the next escape characters (masking out escaped backslashes), along with + * any non-backslash escape codes. + * + * \n \\n \\\n \\\\n returns: + * \n \ \ \n \ \ + * 11 100 1011 10100 + * + * You are expected to mask out the first bit yourself if the previous block had a trailing + * escape. + * + * & the result with potential_escape to get just the escape characters. + * ^ the result with (potential_escape | first_is_escaped) to get escaped characters. + */ + static simdjson_really_inline uint64_t next_escape_and_terminal_code(uint64_t potential_escape) noexcept { + // If we were to just shift and mask out any odd bits, we'd actually get a *half* right answer: + // any even-aligned backslash runs would be correct! Odd-aligned backslash runs would be + // inverted (\\\ would be 010 instead of 101). + // + // ``` + // string: | ____\\\\_\\\\_____ | + // maybe_escaped | ODD | \ \ \ \ | + // even-aligned ^^^ ^^^^ odd-aligned + // ``` + // + // Taking that into account, our basic strategy is: + // + // 1. Use subtraction to produce a mask with 1's for even-aligned runs and 0's for + // odd-aligned runs. + // 2. XOR all odd bits, which masks out the odd bits in even-aligned runs, and brings IN the + // odd bits in odd-aligned runs. + // 3. & with backslash to clean up any stray bits. + // runs are set to 0, and then XORing with "odd": + // + // | | Mask (shows characters instead of 1's) | Instructions | + // |--------------------------------|----------------------------------------|---------------------| + // | string | `\\n_\\\n___\\\n___\\\\___\\\\__\\\` | + // | | ` even odd even odd odd` | + // | maybe_escaped | ` n \\n \\n \\\_ \\\_ \\` X | 1 (potential_escape << 1) + // | maybe_escaped_and_odd | ` \n_ \\n _ \\\n_ _ \\\__ _\\\_ \\\` | 1 (maybe_escaped | odd) + // | even_series_codes_and_odd | ` n_\\\ _ n_ _\\\\ _ _ ` | 1 (maybe_escaped_and_odd - potential_escape) + // | escape_and_terminal_code | ` \n \ \n \ \n \ \ \ \ \ \` | 1 (^ odd) + // - // Escaped characters are characters following an escape. - uint64_t maybe_escaped = potential_escape << 1; + // Escaped characters are characters following an escape. + uint64_t maybe_escaped = potential_escape << 1; - // To distinguish odd from even escape sequences, therefore, we turn on any *starting* - // escapes that are on an odd byte. (We actually bring in all odd bits, for speed.) - // - Odd runs of backslashes are 0000, and the code at the end ("n" in \n or \\n) is 1. - // - Odd runs of backslashes are 1111, and the code at the end ("n" in \n or \\n) is 0. - // - All other odd bytes are 1, and even bytes are 0. - uint64_t maybe_escaped_and_odd_bits = maybe_escaped | ODD_BITS; - uint64_t even_series_codes_and_odd_bits = maybe_escaped_and_odd_bits - potential_escape; + // To distinguish odd from even escape sequences, therefore, we turn on any *starting* + // escapes that are on an odd byte. (We actually bring in all odd bits, for speed.) + // - Odd runs of backslashes are 0000, and the code at the end ("n" in \n or \\n) is 1. + // - Odd runs of backslashes are 1111, and the code at the end ("n" in \n or \\n) is 0. + // - All other odd bytes are 1, and even bytes are 0. + uint64_t maybe_escaped_and_odd_bits = maybe_escaped | ODD_BITS; + uint64_t even_series_codes_and_odd_bits = maybe_escaped_and_odd_bits - potential_escape; - // Now we flip all odd bytes back with xor. This: - // - Makes odd runs of backslashes go from 0000 to 1010 - // - Makes even runs of backslashes go from 1111 to 1010 - // - Sets actually-escaped codes to 1 (the n in \n and \\n: \n = 11, \\n = 100) - // - Resets all other bytes to 0 - return even_series_codes_and_odd_bits ^ ODD_BITS; - } - }; + // Now we flip all odd bytes back with xor. This: + // - Makes odd runs of backslashes go from 0000 to 1010 + // - Makes even runs of backslashes go from 1111 to 1010 + // - Sets actually-escaped codes to 1 (the n in \n and \\n: \n = 11, \\n = 100) + // - Resets all other bytes to 0 + return even_series_codes_and_odd_bits ^ ODD_BITS; + } +}; - } // namespace stage1 - } // unnamed namespace - } // namespace haswell +} // namespace stage1 +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRING_SCANNER_H @@ -17907,93 +19361,93 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace { - namespace stage1 { +namespace haswell { +namespace { +namespace stage1 { - struct json_string_block { - // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 - simdjson_really_inline json_string_block(uint64_t escaped, uint64_t quote, uint64_t in_string) : - _escaped(escaped), _quote(quote), _in_string(in_string) {} +struct json_string_block { + // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 + simdjson_really_inline json_string_block(uint64_t escaped, uint64_t quote, uint64_t in_string) : + _escaped(escaped), _quote(quote), _in_string(in_string) {} - // Escaped characters (characters following an escape() character) - simdjson_really_inline uint64_t escaped() const { return _escaped; } - // Real (non-backslashed) quotes - simdjson_really_inline uint64_t quote() const { return _quote; } - // Only characters inside the string (not including the quotes) - simdjson_really_inline uint64_t string_content() const { return _in_string & ~_quote; } - // Return a mask of whether the given characters are inside a string (only works on non-quotes) - simdjson_really_inline uint64_t non_quote_inside_string(uint64_t mask) const { return mask & _in_string; } - // Return a mask of whether the given characters are inside a string (only works on non-quotes) - simdjson_really_inline uint64_t non_quote_outside_string(uint64_t mask) const { return mask & ~_in_string; } - // Tail of string (everything except the start quote) - simdjson_really_inline uint64_t string_tail() const { return _in_string ^ _quote; } + // Escaped characters (characters following an escape() character) + simdjson_really_inline uint64_t escaped() const { return _escaped; } + // Real (non-backslashed) quotes + simdjson_really_inline uint64_t quote() const { return _quote; } + // Only characters inside the string (not including the quotes) + simdjson_really_inline uint64_t string_content() const { return _in_string & ~_quote; } + // Return a mask of whether the given characters are inside a string (only works on non-quotes) + simdjson_really_inline uint64_t non_quote_inside_string(uint64_t mask) const { return mask & _in_string; } + // Return a mask of whether the given characters are inside a string (only works on non-quotes) + simdjson_really_inline uint64_t non_quote_outside_string(uint64_t mask) const { return mask & ~_in_string; } + // Tail of string (everything except the start quote) + simdjson_really_inline uint64_t string_tail() const { return _in_string ^ _quote; } - // escaped characters (backslashed--does not include the hex characters after \u) - uint64_t _escaped; - // real quotes (non-escaped ones) - uint64_t _quote; - // string characters (includes start quote but not end quote) - uint64_t _in_string; - }; + // escaped characters (backslashed--does not include the hex characters after \u) + uint64_t _escaped; + // real quotes (non-escaped ones) + uint64_t _quote; + // string characters (includes start quote but not end quote) + uint64_t _in_string; +}; - // Scans blocks for string characters, storing the state necessary to do so - class json_string_scanner { - public: - simdjson_really_inline json_string_block next(const simd::simd8x64& in); - // Returns either UNCLOSED_STRING or SUCCESS - simdjson_really_inline error_code finish(); +// Scans blocks for string characters, storing the state necessary to do so +class json_string_scanner { +public: + simdjson_really_inline json_string_block next(const simd::simd8x64& in); + // Returns either UNCLOSED_STRING or SUCCESS + simdjson_really_inline error_code finish(); - private: - // Scans for escape characters - json_escape_scanner escape_scanner{}; - // Whether the last iteration was still inside a string (all 1's = true, all 0's = false). - uint64_t prev_in_string = 0ULL; - }; +private: + // Scans for escape characters + json_escape_scanner escape_scanner{}; + // Whether the last iteration was still inside a string (all 1's = true, all 0's = false). + uint64_t prev_in_string = 0ULL; +}; - // - // Return a mask of all string characters plus end quotes. - // - // prev_escaped is overflow saying whether the next character is escaped. - // prev_in_string is overflow saying whether we're still in a string. - // - // Backslash sequences outside of quotes will be detected in stage 2. - // - simdjson_really_inline json_string_block json_string_scanner::next(const simd::simd8x64& in) { - const uint64_t backslash = in.eq('\\'); - const uint64_t escaped = escape_scanner.next(backslash).escaped; - const uint64_t quote = in.eq('"') & ~escaped; +// +// Return a mask of all string characters plus end quotes. +// +// prev_escaped is overflow saying whether the next character is escaped. +// prev_in_string is overflow saying whether we're still in a string. +// +// Backslash sequences outside of quotes will be detected in stage 2. +// +simdjson_really_inline json_string_block json_string_scanner::next(const simd::simd8x64& in) { + const uint64_t backslash = in.eq('\\'); + const uint64_t escaped = escape_scanner.next(backslash).escaped; + const uint64_t quote = in.eq('"') & ~escaped; - // - // prefix_xor flips on bits inside the string (and flips off the end quote). - // - // Then we xor with prev_in_string: if we were in a string already, its effect is flipped - // (characters inside strings are outside, and characters outside strings are inside). - // - const uint64_t in_string = prefix_xor(quote) ^ prev_in_string; + // + // prefix_xor flips on bits inside the string (and flips off the end quote). + // + // Then we xor with prev_in_string: if we were in a string already, its effect is flipped + // (characters inside strings are outside, and characters outside strings are inside). + // + const uint64_t in_string = prefix_xor(quote) ^ prev_in_string; - // - // Check if we're still in a string at the end of the box so the next block will know - // - prev_in_string = uint64_t(static_cast(in_string) >> 63); + // + // Check if we're still in a string at the end of the box so the next block will know + // + prev_in_string = uint64_t(static_cast(in_string) >> 63); - // Use ^ to turn the beginning quote off, and the end quote on. + // Use ^ to turn the beginning quote off, and the end quote on. - // We are returning a function-local object so either we get a move constructor - // or we get copy elision. - return json_string_block(escaped, quote, in_string); - } + // We are returning a function-local object so either we get a move constructor + // or we get copy elision. + return json_string_block(escaped, quote, in_string); +} - simdjson_really_inline error_code json_string_scanner::finish() { - if (prev_in_string) { - return UNCLOSED_STRING; - } - return SUCCESS; - } +simdjson_really_inline error_code json_string_scanner::finish() { + if (prev_in_string) { + return UNCLOSED_STRING; + } + return SUCCESS; +} - } // namespace stage1 - } // unnamed namespace - } // namespace haswell +} // namespace stage1 +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRING_SCANNER_H @@ -18009,206 +19463,203 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace { - namespace utf8_validation { +namespace haswell { +namespace { +namespace utf8_validation { - using namespace simd; +using namespace simd; - simdjson_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { - // Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) - // Bit 1 = Too Long (ASCII followed by continuation) - // Bit 2 = Overlong 3-byte - // Bit 4 = Surrogate - // Bit 5 = Overlong 2-byte - // Bit 7 = Two Continuations - constexpr const uint8_t TOO_SHORT = 1 << 0; // 11______ 0_______ - // 11______ 11______ - constexpr const uint8_t TOO_LONG = 1 << 1; // 0_______ 10______ - constexpr const uint8_t OVERLONG_3 = 1 << 2; // 11100000 100_____ - constexpr const uint8_t SURROGATE = 1 << 4; // 11101101 101_____ - constexpr const uint8_t OVERLONG_2 = 1 << 5; // 1100000_ 10______ - constexpr const uint8_t TWO_CONTS = 1 << 7; // 10______ 10______ - constexpr const uint8_t TOO_LARGE = 1 << 3; // 11110100 1001____ - // 11110100 101_____ - // 11110101 1001____ - // 11110101 101_____ - // 1111011_ 1001____ - // 1111011_ 101_____ - // 11111___ 1001____ - // 11111___ 101_____ - constexpr const uint8_t TOO_LARGE_1000 = 1 << 6; - // 11110101 1000____ - // 1111011_ 1000____ - // 11111___ 1000____ - constexpr const uint8_t OVERLONG_4 = 1 << 6; // 11110000 1000____ + simdjson_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { +// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) +// Bit 1 = Too Long (ASCII followed by continuation) +// Bit 2 = Overlong 3-byte +// Bit 4 = Surrogate +// Bit 5 = Overlong 2-byte +// Bit 7 = Two Continuations + constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______ + // 11______ 11______ + constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______ + constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____ + constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____ + constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______ + constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______ + constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____ + // 11110100 101_____ + // 11110101 1001____ + // 11110101 101_____ + // 1111011_ 1001____ + // 1111011_ 101_____ + // 11111___ 1001____ + // 11111___ 101_____ + constexpr const uint8_t TOO_LARGE_1000 = 1<<6; + // 11110101 1000____ + // 1111011_ 1000____ + // 11111___ 1000____ + constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____ - const simd8 byte_1_high = prev1.shr<4>().lookup_16( - // 0_______ ________ - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - // 10______ ________ - TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, - // 1100____ ________ - TOO_SHORT | OVERLONG_2, - // 1101____ ________ - TOO_SHORT, - // 1110____ ________ - TOO_SHORT | OVERLONG_3 | SURROGATE, - // 1111____ ________ - TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 - ); - constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . - const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( - // ____0000 ________ - CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, - // ____0001 ________ - CARRY | OVERLONG_2, - // ____001_ ________ - CARRY, - CARRY, + const simd8 byte_1_high = prev1.shr<4>().lookup_16( + // 0_______ ________ + TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, + TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, + // 10______ ________ + TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, + // 1100____ ________ + TOO_SHORT | OVERLONG_2, + // 1101____ ________ + TOO_SHORT, + // 1110____ ________ + TOO_SHORT | OVERLONG_3 | SURROGATE, + // 1111____ ________ + TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 + ); + constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . + const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( + // ____0000 ________ + CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, + // ____0001 ________ + CARRY | OVERLONG_2, + // ____001_ ________ + CARRY, + CARRY, - // ____0100 ________ - CARRY | TOO_LARGE, - // ____0101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____011_ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, + // ____0100 ________ + CARRY | TOO_LARGE, + // ____0101 ________ + CARRY | TOO_LARGE | TOO_LARGE_1000, + // ____011_ ________ + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____1___ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____1101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000 - ); - const simd8 byte_2_high = input.shr<4>().lookup_16( - // ________ 0_______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, + // ____1___ ________ + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + // ____1101 ________ + CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000 + ); + const simd8 byte_2_high = input.shr<4>().lookup_16( + // ________ 0_______ + TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, + TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - // ________ 1000____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, - // ________ 1001____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, - // ________ 101_____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, + // ________ 1000____ + TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, + // ________ 1001____ + TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, + // ________ 101_____ + TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, + TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - // ________ 11______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT - ); - return (byte_1_high & byte_1_low & byte_2_high); - } - simdjson_inline simd8 check_multibyte_lengths(const simd8 input, - const simd8 prev_input, const simd8 sc) { - simd8 prev2 = input.prev<2>(prev_input); - simd8 prev3 = input.prev<3>(prev_input); - simd8 must23 = must_be_2_3_continuation(prev2, prev3); - simd8 must23_80 = must23 & uint8_t(0x80); - return must23_80 ^ sc; - } + // ________ 11______ + TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT + ); + return (byte_1_high & byte_1_low & byte_2_high); + } + simdjson_inline simd8 check_multibyte_lengths(const simd8 input, + const simd8 prev_input, const simd8 sc) { + simd8 prev2 = input.prev<2>(prev_input); + simd8 prev3 = input.prev<3>(prev_input); + simd8 must23 = must_be_2_3_continuation(prev2, prev3); + simd8 must23_80 = must23 & uint8_t(0x80); + return must23_80 ^ sc; + } - // - // Return nonzero if there are incomplete multibyte characters at the end of the block: - // e.g. if there is a 4-byte character, but it's 3 bytes from the end. - // - simdjson_inline simd8 is_incomplete(const simd8 input) { - // If the previous input's last 3 bytes match this, they're too short (they ended at EOF): - // ... 1111____ 111_____ 11______ -#if SIMDJSON_IMPLEMENTATION_ICELAKE - static const uint8_t max_array[64] = { - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0xf0u - 1, 0xe0u - 1, 0xc0u - 1 - }; + // + // Return nonzero if there are incomplete multibyte characters at the end of the block: + // e.g. if there is a 4-byte character, but it's 3 bytes from the end. + // + simdjson_inline simd8 is_incomplete(const simd8 input) { + // If the previous input's last 3 bytes match this, they're too short (they ended at EOF): + // ... 1111____ 111_____ 11______ +#if SIMDJSON_IMPLEMENTATION_ICELAKE || (SIMDJSON_IMPLEMENTATION_RVV_VLS && __riscv_v_fixed_vlen >= 512) + static const uint8_t max_array[64] = { + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0xf0u-1, 0xe0u-1, 0xc0u-1 + }; #else - static const uint8_t max_array[32] = { - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0xf0u - 1, 0xe0u - 1, 0xc0u - 1 - }; + static const uint8_t max_array[32] = { + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0xf0u-1, 0xe0u-1, 0xc0u-1 + }; #endif - const simd8 max_value(&max_array[sizeof(max_array) - sizeof(simd8)]); - return input.gt_bits(max_value); - } + const simd8 max_value(&max_array[sizeof(max_array)-sizeof(simd8)]); + return input.gt_bits(max_value); + } - struct utf8_checker { - // If this is nonzero, there has been a UTF-8 error. - simd8 error; - // The last input we received - simd8 prev_input_block; - // Whether the last input we received was incomplete (used for ASCII fast path) - simd8 prev_incomplete; + struct utf8_checker { + // If this is nonzero, there has been a UTF-8 error. + simd8 error; + // The last input we received + simd8 prev_input_block; + // Whether the last input we received was incomplete (used for ASCII fast path) + simd8 prev_incomplete; - // - // Check whether the current bytes are valid UTF-8. - // - simdjson_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { - // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes - // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) - simd8 prev1 = input.prev<1>(prev_input); - simd8 sc = check_special_cases(input, prev1); - this->error |= check_multibyte_lengths(input, prev_input, sc); - } + // + // Check whether the current bytes are valid UTF-8. + // + simdjson_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { + // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes + // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) + simd8 prev1 = input.prev<1>(prev_input); + simd8 sc = check_special_cases(input, prev1); + this->error |= check_multibyte_lengths(input, prev_input, sc); + } - // The only problem that can happen at EOF is that a multibyte character is too short - // or a byte value too large in the last bytes: check_special_cases only checks for bytes - // too large in the first of two bytes. - simdjson_inline void check_eof() { - // If the previous block had incomplete UTF-8 characters at the end, an ASCII block can't - // possibly finish them. - this->error |= this->prev_incomplete; - } + // The only problem that can happen at EOF is that a multibyte character is too short + // or a byte value too large in the last bytes: check_special_cases only checks for bytes + // too large in the first of two bytes. + simdjson_inline void check_eof() { + // If the previous block had incomplete UTF-8 characters at the end, an ASCII block can't + // possibly finish them. + this->error |= this->prev_incomplete; + } - simdjson_inline void check_next_input(const simd8x64& input) { - if (simdjson_likely(is_ascii(input))) { - this->error |= this->prev_incomplete; - } - else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 1) - || (simd8x64::NUM_CHUNKS == 2) - || (simd8x64::NUM_CHUNKS == 4), - "We support one, two or four chunks per 64-byte block."); - SIMDJSON_IF_CONSTEXPR(simd8x64::NUM_CHUNKS == 1) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - } -else SIMDJSON_IF_CONSTEXPR(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); + simdjson_inline void check_next_input(const simd8x64& input) { + if(simdjson_likely(is_ascii(input))) { + this->error |= this->prev_incomplete; + } else { + // you might think that a for-loop would work, but under Visual Studio, it is not good enough. + static_assert((simd8x64::NUM_CHUNKS == 1) + ||(simd8x64::NUM_CHUNKS == 2) + || (simd8x64::NUM_CHUNKS == 4), + "We support one, two or four chunks per 64-byte block."); + SIMDJSON_IF_CONSTEXPR (simd8x64::NUM_CHUNKS == 1) { + this->check_utf8_bytes(input.get<0>(), this->prev_input_block); + } else SIMDJSON_IF_CONSTEXPR (simd8x64::NUM_CHUNKS == 2) { + this->check_utf8_bytes(input.get<0>(), this->prev_input_block); + this->check_utf8_bytes(input.get<1>(), input.get<0>()); + } else SIMDJSON_IF_CONSTEXPR (simd8x64::NUM_CHUNKS == 4) { + this->check_utf8_bytes(input.get<0>(), this->prev_input_block); + this->check_utf8_bytes(input.get<1>(), input.get<0>()); + this->check_utf8_bytes(input.get<2>(), input.get<1>()); + this->check_utf8_bytes(input.get<3>(), input.get<2>()); } - else SIMDJSON_IF_CONSTEXPR(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - this->prev_incomplete = is_incomplete(input.chunks[simd8x64::NUM_CHUNKS - 1]); - this->prev_input_block = input.chunks[simd8x64::NUM_CHUNKS - 1]; - } - } - // do not forget to call check_eof! - simdjson_inline error_code errors() { - return this->error.any_bits_set_anywhere() ? error_code::UTF8_ERROR : error_code::SUCCESS; - } + this->prev_incomplete = is_incomplete(input.get::NUM_CHUNKS-1>()); + this->prev_input_block = input.get::NUM_CHUNKS-1>(); + } + } + // do not forget to call check_eof! + simdjson_warn_unused simdjson_inline error_code errors() { + return this->error.any_bits_set_anywhere() ? error_code::UTF8_ERROR : error_code::SUCCESS; + } - }; // struct utf8_checker - } // namespace utf8_validation + }; // struct utf8_checker +} // namespace utf8_validation - } // unnamed namespace - } // namespace haswell +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_UTF8_LOOKUP4_ALGORITHM_H @@ -18225,161 +19676,161 @@ else SIMDJSON_IF_CONSTEXPR(simd8x64::NUM_CHUNKS == 2) { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace { - namespace stage1 { +namespace haswell { +namespace { +namespace stage1 { - /** - * A block of scanned json, with information on operators and scalars. - * - * We seek to identify pseudo-structural characters. Anything that is inside - * a string must be omitted (hence & ~_string.string_tail()). - * Otherwise, pseudo-structural characters come in two forms. - * 1. We have the structural characters ([,],{,},:, comma). The - * term 'structural character' is from the JSON RFC. - * 2. We have the 'scalar pseudo-structural characters'. - * Scalars are quotes, and any character except structural characters and white space. - * - * To identify the scalar pseudo-structural characters, we must look at what comes - * before them: it must be a space, a quote or a structural characters. - * Starting with simdjson v0.3, we identify them by - * negation: we identify everything that is followed by a non-quote scalar, - * and we negate that. Whatever remains must be a 'scalar pseudo-structural character'. - */ - struct json_block { - public: - // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 - simdjson_inline json_block(json_string_block&& string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : - _string(std::move(string)), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} - simdjson_inline json_block(json_string_block string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : - _string(string), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} +/** + * A block of scanned json, with information on operators and scalars. + * + * We seek to identify pseudo-structural characters. Anything that is inside + * a string must be omitted (hence & ~_string.string_tail()). + * Otherwise, pseudo-structural characters come in two forms. + * 1. We have the structural characters ([,],{,},:, comma). The + * term 'structural character' is from the JSON RFC. + * 2. We have the 'scalar pseudo-structural characters'. + * Scalars are quotes, and any character except structural characters and white space. + * + * To identify the scalar pseudo-structural characters, we must look at what comes + * before them: it must be a space, a quote or a structural characters. + * Starting with simdjson v0.3, we identify them by + * negation: we identify everything that is followed by a non-quote scalar, + * and we negate that. Whatever remains must be a 'scalar pseudo-structural character'. + */ +struct json_block { +public: + // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 + simdjson_inline json_block(json_string_block&& string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : + _string(std::move(string)), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} + simdjson_inline json_block(json_string_block string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : + _string(string), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} - /** - * The start of structurals. - * In simdjson prior to v0.3, these were called the pseudo-structural characters. - **/ - simdjson_inline uint64_t structural_start() const noexcept { return potential_structural_start() & ~_string.string_tail(); } - /** All JSON whitespace (i.e. not in a string) */ - simdjson_inline uint64_t whitespace() const noexcept { return non_quote_outside_string(_characters.whitespace()); } + /** + * The start of structurals. + * In simdjson prior to v0.3, these were called the pseudo-structural characters. + **/ + simdjson_inline uint64_t structural_start() const noexcept { return potential_structural_start() & ~_string.string_tail(); } + /** All JSON whitespace (i.e. not in a string) */ + simdjson_inline uint64_t whitespace() const noexcept { return non_quote_outside_string(_characters.whitespace()); } - // Helpers + // Helpers - /** Whether the given characters are inside a string (only works on non-quotes) */ - simdjson_inline uint64_t non_quote_inside_string(uint64_t mask) const noexcept { return _string.non_quote_inside_string(mask); } - /** Whether the given characters are outside a string (only works on non-quotes) */ - simdjson_inline uint64_t non_quote_outside_string(uint64_t mask) const noexcept { return _string.non_quote_outside_string(mask); } + /** Whether the given characters are inside a string (only works on non-quotes) */ + simdjson_inline uint64_t non_quote_inside_string(uint64_t mask) const noexcept { return _string.non_quote_inside_string(mask); } + /** Whether the given characters are outside a string (only works on non-quotes) */ + simdjson_inline uint64_t non_quote_outside_string(uint64_t mask) const noexcept { return _string.non_quote_outside_string(mask); } - // string and escape characters - json_string_block _string; - // whitespace, structural characters ('operators'), scalars - json_character_block _characters; - // whether the previous character was a scalar - uint64_t _follows_potential_nonquote_scalar; - private: - // Potential structurals (i.e. disregarding strings) + // string and escape characters + json_string_block _string; + // whitespace, structural characters ('operators'), scalars + json_character_block _characters; + // whether the previous character was a scalar + uint64_t _follows_potential_nonquote_scalar; +private: + // Potential structurals (i.e. disregarding strings) - /** - * structural elements ([,],{,},:, comma) plus scalar starts like 123, true and "abc". - * They may reside inside a string. - **/ - simdjson_inline uint64_t potential_structural_start() const noexcept { return _characters.op() | potential_scalar_start(); } - /** - * The start of non-operator runs, like 123, true and "abc". - * It main reside inside a string. - **/ - simdjson_inline uint64_t potential_scalar_start() const noexcept { - // The term "scalar" refers to anything except structural characters and white space - // (so letters, numbers, quotes). - // Whenever it is preceded by something that is not a structural element ({,},[,],:, ") nor a white-space - // then we know that it is irrelevant structurally. - return _characters.scalar() & ~follows_potential_scalar(); - } - /** - * Whether the given character is immediately after a non-operator like 123, true. - * The characters following a quote are not included. - */ - simdjson_inline uint64_t follows_potential_scalar() const noexcept { - // _follows_potential_nonquote_scalar: is defined as marking any character that follows a character - // that is not a structural element ({,},[,],:, comma) nor a quote (") and that is not a - // white space. - // It is understood that within quoted region, anything at all could be marked (irrelevant). - return _follows_potential_nonquote_scalar; - } - }; + /** + * structural elements ([,],{,},:, comma) plus scalar starts like 123, true and "abc". + * They may reside inside a string. + **/ + simdjson_inline uint64_t potential_structural_start() const noexcept { return _characters.op() | potential_scalar_start(); } + /** + * The start of non-operator runs, like 123, true and "abc". + * It main reside inside a string. + **/ + simdjson_inline uint64_t potential_scalar_start() const noexcept { + // The term "scalar" refers to anything except structural characters and white space + // (so letters, numbers, quotes). + // Whenever it is preceded by something that is not a structural element ({,},[,],:, ") nor a white-space + // then we know that it is irrelevant structurally. + return _characters.scalar() & ~follows_potential_scalar(); + } + /** + * Whether the given character is immediately after a non-operator like 123, true. + * The characters following a quote are not included. + */ + simdjson_inline uint64_t follows_potential_scalar() const noexcept { + // _follows_potential_nonquote_scalar: is defined as marking any character that follows a character + // that is not a structural element ({,},[,],:, comma) nor a quote (") and that is not a + // white space. + // It is understood that within quoted region, anything at all could be marked (irrelevant). + return _follows_potential_nonquote_scalar; + } +}; - /** - * Scans JSON for important bits: structural characters or 'operators', strings, and scalars. - * - * The scanner starts by calculating two distinct things: - * - string characters (taking \" into account) - * - structural characters or 'operators' ([]{},:, comma) - * and scalars (runs of non-operators like 123, true and "abc") - * - * To minimize data dependency (a key component of the scanner's speed), it finds these in parallel: - * in particular, the operator/scalar bit will find plenty of things that are actually part of - * strings. When we're done, json_block will fuse the two together by masking out tokens that are - * part of a string. - */ - class json_scanner { - public: - json_scanner() = default; - simdjson_inline json_block next(const simd::simd8x64& in); - // Returns either UNCLOSED_STRING or SUCCESS - simdjson_inline error_code finish(); +/** + * Scans JSON for important bits: structural characters or 'operators', strings, and scalars. + * + * The scanner starts by calculating two distinct things: + * - string characters (taking \" into account) + * - structural characters or 'operators' ([]{},:, comma) + * and scalars (runs of non-operators like 123, true and "abc") + * + * To minimize data dependency (a key component of the scanner's speed), it finds these in parallel: + * in particular, the operator/scalar bit will find plenty of things that are actually part of + * strings. When we're done, json_block will fuse the two together by masking out tokens that are + * part of a string. + */ +class json_scanner { +public: + json_scanner() = default; + simdjson_inline json_block next(const simd::simd8x64& in); + // Returns either UNCLOSED_STRING or SUCCESS + simdjson_warn_unused simdjson_inline error_code finish(); - private: - // Whether the last character of the previous iteration is part of a scalar token - // (anything except whitespace or a structural character/'operator'). - uint64_t prev_scalar = 0ULL; - json_string_scanner string_scanner{}; - }; +private: + // Whether the last character of the previous iteration is part of a scalar token + // (anything except whitespace or a structural character/'operator'). + uint64_t prev_scalar = 0ULL; + json_string_scanner string_scanner{}; +}; - // - // Check if the current character immediately follows a matching character. - // - // For example, this checks for quotes with backslashes in front of them: - // - // const uint64_t backslashed_quote = in.eq('"') & immediately_follows(in.eq('\'), prev_backslash); - // - simdjson_inline uint64_t follows(const uint64_t match, uint64_t& overflow) { - const uint64_t result = match << 1 | overflow; - overflow = match >> 63; - return result; - } +// +// Check if the current character immediately follows a matching character. +// +// For example, this checks for quotes with backslashes in front of them: +// +// const uint64_t backslashed_quote = in.eq('"') & immediately_follows(in.eq('\'), prev_backslash); +// +simdjson_inline uint64_t follows(const uint64_t match, uint64_t &overflow) { + const uint64_t result = match << 1 | overflow; + overflow = match >> 63; + return result; +} - simdjson_inline json_block json_scanner::next(const simd::simd8x64& in) { - json_string_block strings = string_scanner.next(in); - // identifies the white-space and the structural characters - json_character_block characters = json_character_block::classify(in); - // The term "scalar" refers to anything except structural characters and white space - // (so letters, numbers, quotes). - // We want follows_scalar to mark anything that follows a non-quote scalar (so letters and numbers). - // - // A terminal quote should either be followed by a structural character (comma, brace, bracket, colon) - // or nothing. However, we still want ' "a string"true ' to mark the 't' of 'true' as a potential - // pseudo-structural character just like we would if we had ' "a string" true '; otherwise we - // may need to add an extra check when parsing strings. - // - // Performance: there are many ways to skin this cat. - const uint64_t nonquote_scalar = characters.scalar() & ~strings.quote(); - uint64_t follows_nonquote_scalar = follows(nonquote_scalar, prev_scalar); - // We are returning a function-local object so either we get a move constructor - // or we get copy elision. - return json_block( - strings,// strings is a function-local object so either it moves or the copy is elided. - characters, - follows_nonquote_scalar - ); - } +simdjson_inline json_block json_scanner::next(const simd::simd8x64& in) { + json_string_block strings = string_scanner.next(in); + // identifies the white-space and the structural characters + json_character_block characters = json_character_block::classify(in); + // The term "scalar" refers to anything except structural characters and white space + // (so letters, numbers, quotes). + // We want follows_scalar to mark anything that follows a non-quote scalar (so letters and numbers). + // + // A terminal quote should either be followed by a structural character (comma, brace, bracket, colon) + // or nothing. However, we still want ' "a string"true ' to mark the 't' of 'true' as a potential + // pseudo-structural character just like we would if we had ' "a string" true '; otherwise we + // may need to add an extra check when parsing strings. + // + // Performance: there are many ways to skin this cat. + const uint64_t nonquote_scalar = characters.scalar() & ~strings.quote(); + uint64_t follows_nonquote_scalar = follows(nonquote_scalar, prev_scalar); + // We are returning a function-local object so either we get a move constructor + // or we get copy elision. + return json_block( + strings,// strings is a function-local object so either it moves or the copy is elided. + characters, + follows_nonquote_scalar + ); +} - simdjson_inline error_code json_scanner::finish() { - return string_scanner.finish(); - } +simdjson_warn_unused simdjson_inline error_code json_scanner::finish() { + return string_scanner.finish(); +} - } // namespace stage1 - } // unnamed namespace - } // namespace haswell +} // namespace stage1 +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_SCANNER_H @@ -18397,99 +19848,99 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace { - namespace stage1 { +namespace haswell { +namespace { +namespace stage1 { - /** - * This algorithm is used to quickly identify the last structural position that - * makes up a complete document. - * - * It does this by going backwards and finding the last *document boundary* (a - * place where one value follows another without a comma between them). If the - * last document (the characters after the boundary) has an equal number of - * start and end brackets, it is considered complete. - * - * Simply put, we iterate over the structural characters, starting from - * the end. We consider that we found the end of a JSON document when the - * first element of the pair is NOT one of these characters: '{' '[' ':' ',' - * and when the second element is NOT one of these characters: '}' ']' ':' ','. - * - * This simple comparison works most of the time, but it does not cover cases - * where the batch's structural indexes contain a perfect amount of documents. - * In such a case, we do not have access to the structural index which follows - * the last document, therefore, we do not have access to the second element in - * the pair, and that means we cannot identify the last document. To fix this - * issue, we keep a count of the open and closed curly/square braces we found - * while searching for the pair. When we find a pair AND the count of open and - * closed curly/square braces is the same, we know that we just passed a - * complete document, therefore the last json buffer location is the end of the - * batch. - */ - simdjson_inline uint32_t find_next_document_index(dom_parser_implementation& parser) { - // Variant: do not count separately, just figure out depth - if (parser.n_structural_indexes == 0) { return 0; } - auto arr_cnt = 0; - auto obj_cnt = 0; - for (auto i = parser.n_structural_indexes - 1; i > 0; i--) { - auto idxb = parser.structural_indexes[i]; - switch (parser.buf[idxb]) { - case ':': - case ',': - continue; - case '}': - obj_cnt--; - continue; - case ']': - arr_cnt--; - continue; - case '{': - obj_cnt++; - break; - case '[': - arr_cnt++; - break; - } - auto idxa = parser.structural_indexes[i - 1]; - switch (parser.buf[idxa]) { - case '{': - case '[': - case ':': - case ',': - continue; - } - // Last document is complete, so the next document will appear after! - if (!arr_cnt && !obj_cnt) { - return parser.n_structural_indexes; - } - // Last document is incomplete; mark the document at i + 1 as the next one - return i; - } - // If we made it to the end, we want to finish counting to see if we have a full document. - switch (parser.buf[parser.structural_indexes[0]]) { - case '}': - obj_cnt--; - break; - case ']': - arr_cnt--; - break; - case '{': - obj_cnt++; - break; - case '[': - arr_cnt++; - break; - } - if (!arr_cnt && !obj_cnt) { - // We have a complete document. - return parser.n_structural_indexes; - } - return 0; - } +/** + * This algorithm is used to quickly identify the last structural position that + * makes up a complete document. + * + * It does this by going backwards and finding the last *document boundary* (a + * place where one value follows another without a comma between them). If the + * last document (the characters after the boundary) has an equal number of + * start and end brackets, it is considered complete. + * + * Simply put, we iterate over the structural characters, starting from + * the end. We consider that we found the end of a JSON document when the + * first element of the pair is NOT one of these characters: '{' '[' ':' ',' + * and when the second element is NOT one of these characters: '}' ']' ':' ','. + * + * This simple comparison works most of the time, but it does not cover cases + * where the batch's structural indexes contain a perfect amount of documents. + * In such a case, we do not have access to the structural index which follows + * the last document, therefore, we do not have access to the second element in + * the pair, and that means we cannot identify the last document. To fix this + * issue, we keep a count of the open and closed curly/square braces we found + * while searching for the pair. When we find a pair AND the count of open and + * closed curly/square braces is the same, we know that we just passed a + * complete document, therefore the last json buffer location is the end of the + * batch. + */ +simdjson_inline uint32_t find_next_document_index(dom_parser_implementation &parser) { + // Variant: do not count separately, just figure out depth + if(parser.n_structural_indexes == 0) { return 0; } + auto arr_cnt = 0; + auto obj_cnt = 0; + for (auto i = parser.n_structural_indexes - 1; i > 0; i--) { + auto idxb = parser.structural_indexes[i]; + switch (parser.buf[idxb]) { + case ':': + case ',': + continue; + case '}': + obj_cnt--; + continue; + case ']': + arr_cnt--; + continue; + case '{': + obj_cnt++; + break; + case '[': + arr_cnt++; + break; + } + auto idxa = parser.structural_indexes[i - 1]; + switch (parser.buf[idxa]) { + case '{': + case '[': + case ':': + case ',': + continue; + } + // Last document is complete, so the next document will appear after! + if (!arr_cnt && !obj_cnt) { + return parser.n_structural_indexes; + } + // Last document is incomplete; mark the document at i + 1 as the next one + return i; + } + // If we made it to the end, we want to finish counting to see if we have a full document. + switch (parser.buf[parser.structural_indexes[0]]) { + case '}': + obj_cnt--; + break; + case ']': + arr_cnt--; + break; + case '{': + obj_cnt++; + break; + case '[': + arr_cnt++; + break; + } + if (!arr_cnt && !obj_cnt) { + // We have a complete document. + return parser.n_structural_indexes; + } + return 0; +} - } // namespace stage1 - } // unnamed namespace - } // namespace haswell +} // namespace stage1 +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_FIND_NEXT_DOCUMENT_INDEX_H @@ -18511,92 +19962,92 @@ namespace simdjson { // "simdjson/stage1.h" (this simplifies amalgation) namespace simdjson { - namespace haswell { - namespace { - namespace stage1 { +namespace haswell { +namespace { +namespace stage1 { - class json_minifier { - public: - template - static error_code minify(const uint8_t* buf, size_t len, uint8_t* dst, size_t& dst_len) noexcept; +class json_minifier { +public: + template + static error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) noexcept; - private: - simdjson_inline json_minifier(uint8_t* _dst) - : dst{ _dst } - {} - template - simdjson_inline void step(const uint8_t* block_buf, buf_block_reader& reader) noexcept; - simdjson_inline void next(const simd::simd8x64& in, const json_block& block); - simdjson_inline error_code finish(uint8_t* dst_start, size_t& dst_len); - json_scanner scanner{}; - uint8_t* dst; - }; +private: + simdjson_inline json_minifier(uint8_t *_dst) + : dst{_dst} + {} + template + simdjson_inline void step(const uint8_t *block_buf, buf_block_reader &reader) noexcept; + simdjson_inline void next(const simd::simd8x64& in, const json_block& block); + simdjson_warn_unused simdjson_inline error_code finish(uint8_t *dst_start, size_t &dst_len); + json_scanner scanner{}; + uint8_t *dst; +}; - simdjson_inline void json_minifier::next(const simd::simd8x64& in, const json_block& block) { - uint64_t mask = block.whitespace(); - dst += in.compress(mask, dst); - } +simdjson_inline void json_minifier::next(const simd::simd8x64& in, const json_block& block) { + uint64_t mask = block.whitespace(); + dst += in.compress(mask, dst); +} - simdjson_inline error_code json_minifier::finish(uint8_t* dst_start, size_t& dst_len) { - error_code error = scanner.finish(); - if (error) { dst_len = 0; return error; } - dst_len = dst - dst_start; - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code json_minifier::finish(uint8_t *dst_start, size_t &dst_len) { + error_code error = scanner.finish(); + if (error) { dst_len = 0; return error; } + dst_len = dst - dst_start; + return SUCCESS; +} - template<> - simdjson_inline void json_minifier::step<128>(const uint8_t* block_buf, buf_block_reader<128>& reader) noexcept { - simd::simd8x64 in_1(block_buf); - simd::simd8x64 in_2(block_buf + 64); - json_block block_1 = scanner.next(in_1); - json_block block_2 = scanner.next(in_2); - this->next(in_1, block_1); - this->next(in_2, block_2); - reader.advance(); - } +template<> +simdjson_inline void json_minifier::step<128>(const uint8_t *block_buf, buf_block_reader<128> &reader) noexcept { + simd::simd8x64 in_1(block_buf); + simd::simd8x64 in_2(block_buf+64); + json_block block_1 = scanner.next(in_1); + json_block block_2 = scanner.next(in_2); + this->next(in_1, block_1); + this->next(in_2, block_2); + reader.advance(); +} - template<> - simdjson_inline void json_minifier::step<64>(const uint8_t* block_buf, buf_block_reader<64>& reader) noexcept { - simd::simd8x64 in_1(block_buf); - json_block block_1 = scanner.next(in_1); - this->next(block_buf, block_1); - reader.advance(); - } +template<> +simdjson_inline void json_minifier::step<64>(const uint8_t *block_buf, buf_block_reader<64> &reader) noexcept { + simd::simd8x64 in_1(block_buf); + json_block block_1 = scanner.next(in_1); + this->next(block_buf, block_1); + reader.advance(); +} - template - error_code json_minifier::minify(const uint8_t* buf, size_t len, uint8_t* dst, size_t& dst_len) noexcept { - buf_block_reader reader(buf, len); - json_minifier minifier(dst); +template +error_code json_minifier::minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) noexcept { + buf_block_reader reader(buf, len); + json_minifier minifier(dst); - // Index the first n-1 blocks - while (reader.has_full_block()) { - minifier.step(reader.full_block(), reader); - } + // Index the first n-1 blocks + while (reader.has_full_block()) { + minifier.step(reader.full_block(), reader); + } - // Index the last (remainder) block, padded with spaces - uint8_t block[STEP_SIZE]; - size_t remaining_bytes = reader.get_remainder(block); - if (remaining_bytes > 0) { - // We do not want to write directly to the output stream. Rather, we write - // to a local buffer (for safety). - uint8_t out_block[STEP_SIZE]; - uint8_t* const guarded_dst{ minifier.dst }; - minifier.dst = out_block; - minifier.step(block, reader); - size_t to_write = minifier.dst - out_block; - // In some cases, we could be enticed to consider the padded spaces - // as part of the string. This is fine as long as we do not write more - // than we consumed. - if (to_write > remaining_bytes) { to_write = remaining_bytes; } - memcpy(guarded_dst, out_block, to_write); - minifier.dst = guarded_dst + to_write; - } - return minifier.finish(dst, dst_len); - } + // Index the last (remainder) block, padded with spaces + uint8_t block[STEP_SIZE]; + size_t remaining_bytes = reader.get_remainder(block); + if (remaining_bytes > 0) { + // We do not want to write directly to the output stream. Rather, we write + // to a local buffer (for safety). + uint8_t out_block[STEP_SIZE]; + uint8_t * const guarded_dst{minifier.dst}; + minifier.dst = out_block; + minifier.step(block, reader); + size_t to_write = minifier.dst - out_block; + // In some cases, we could be enticed to consider the padded spaces + // as part of the string. This is fine as long as we do not write more + // than we consumed. + if(to_write > remaining_bytes) { to_write = remaining_bytes; } + memcpy(guarded_dst, out_block, to_write); + minifier.dst = guarded_dst + to_write; + } + return minifier.finish(dst, dst_len); +} - } // namespace stage1 - } // unnamed namespace - } // namespace haswell +} // namespace stage1 +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_MINIFIER_H @@ -18622,341 +20073,339 @@ namespace simdjson { // "simdjson/stage1.h" (this simplifies amalgation) namespace simdjson { - namespace haswell { - namespace { - namespace stage1 { +namespace haswell { +namespace { +namespace stage1 { - class bit_indexer { - public: - uint32_t* tail; +class bit_indexer { +public: + uint32_t *tail; - simdjson_inline bit_indexer(uint32_t* index_buf) : tail(index_buf) {} + simdjson_inline bit_indexer(uint32_t *index_buf) : tail(index_buf) {} #if SIMDJSON_PREFER_REVERSE_BITS - /** - * ARM lacks a fast trailing zero instruction, but it has a fast - * bit reversal instruction and a fast leading zero instruction. - * Thus it may be profitable to reverse the bits (once) and then - * to rely on a sequence of instructions that call the leading - * zero instruction. - * - * Performance notes: - * The chosen routine is not optimal in terms of data dependency - * since zero_leading_bit might require two instructions. However, - * it tends to minimize the total number of instructions which is - * beneficial. - */ - simdjson_inline void write_index(uint32_t idx, uint64_t& rev_bits, int i) { - int lz = leading_zeroes(rev_bits); - this->tail[i] = static_cast(idx) + lz; - rev_bits = zero_leading_bit(rev_bits, lz); - } + /** + * ARM lacks a fast trailing zero instruction, but it has a fast + * bit reversal instruction and a fast leading zero instruction. + * Thus it may be profitable to reverse the bits (once) and then + * to rely on a sequence of instructions that call the leading + * zero instruction. + * + * Performance notes: + * The chosen routine is not optimal in terms of data dependency + * since zero_leading_bit might require two instructions. However, + * it tends to minimize the total number of instructions which is + * beneficial. + */ + simdjson_inline void write_index(uint32_t idx, uint64_t& rev_bits, int i) { + int lz = leading_zeroes(rev_bits); + this->tail[i] = static_cast(idx) + lz; + rev_bits = zero_leading_bit(rev_bits, lz); + } #else - /** - * Under recent x64 systems, we often have both a fast trailing zero - * instruction and a fast 'clear-lower-bit' instruction so the following - * algorithm can be competitive. - */ + /** + * Under recent x64 systems, we often have both a fast trailing zero + * instruction and a fast 'clear-lower-bit' instruction so the following + * algorithm can be competitive. + */ - simdjson_inline void write_index(uint32_t idx, uint64_t& bits, int i) { - this->tail[i] = idx + trailing_zeroes(bits); - bits = clear_lowest_bit(bits); - } + simdjson_inline void write_index(uint32_t idx, uint64_t& bits, int i) { + this->tail[i] = idx + trailing_zeroes(bits); + bits = clear_lowest_bit(bits); + } #endif // SIMDJSON_PREFER_REVERSE_BITS - template - simdjson_inline int write_indexes(uint32_t idx, uint64_t& bits) { - write_index(idx, bits, START); - SIMDJSON_IF_CONSTEXPR(N > 1) { - write_indexes<(N - 1 > 0 ? START + 1 : START), (N - 1 >= 0 ? N - 1 : 1)>(idx, bits); - } - return START + N; - } + template + simdjson_inline int write_indexes(uint32_t idx, uint64_t& bits) { + write_index(idx, bits, START); + SIMDJSON_IF_CONSTEXPR (N > 1) { + write_indexes<(N-1>0?START+1:START), (N-1>=0?N-1:1)>(idx, bits); + } + return START+N; + } - template - simdjson_inline int write_indexes_stepped(uint32_t idx, uint64_t& bits, int cnt) { - write_indexes(idx, bits); - SIMDJSON_IF_CONSTEXPR((START + STEP) < END) { - if (simdjson_unlikely((START + STEP) < cnt)) { - write_indexes_stepped<(START + STEP < END ? START + STEP : END), END, STEP>(idx, bits, cnt); - } - } - return ((END - START) % STEP) == 0 ? END : (END - START) - ((END - START) % STEP) + STEP; - } + template + simdjson_inline int write_indexes_stepped(uint32_t idx, uint64_t& bits, int cnt) { + write_indexes(idx, bits); + SIMDJSON_IF_CONSTEXPR ((START+STEP) < END) { + if (simdjson_unlikely((START+STEP) < cnt)) { + write_indexes_stepped<(START+STEP(idx, bits, cnt); + } + } + return ((END-START) % STEP) == 0 ? END : (END-START) - ((END-START) % STEP) + STEP; + } - // flatten out values in 'bits' assuming that they are are to have values of idx - // plus their position in the bitvector, and store these indexes at - // base_ptr[base] incrementing base as we go - // will potentially store extra values beyond end of valid bits, so base_ptr - // needs to be large enough to handle this - // - // If the kernel sets SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER, then it - // will provide its own version of the code. + // flatten out values in 'bits' assuming that they are are to have values of idx + // plus their position in the bitvector, and store these indexes at + // base_ptr[base] incrementing base as we go + // will potentially store extra values beyond end of valid bits, so base_ptr + // needs to be large enough to handle this + // + // If the kernel sets SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER, then it + // will provide its own version of the code. #ifdef SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER - simdjson_inline void write(uint32_t idx, uint64_t bits); + simdjson_inline void write(uint32_t idx, uint64_t bits); #else - simdjson_inline void write(uint32_t idx, uint64_t bits) { - // In some instances, the next branch is expensive because it is mispredicted. - // Unfortunately, in other cases, - // it helps tremendously. - if (bits == 0) - return; + simdjson_inline void write(uint32_t idx, uint64_t bits) { + // In some instances, the next branch is expensive because it is mispredicted. + // Unfortunately, in other cases, + // it helps tremendously. + if (bits == 0) + return; - int cnt = static_cast(count_ones(bits)); + int cnt = static_cast(count_ones(bits)); #if SIMDJSON_PREFER_REVERSE_BITS - bits = reverse_bits(bits); + bits = reverse_bits(bits); #endif #ifdef SIMDJSON_STRUCTURAL_INDEXER_STEP - static constexpr const int STEP = SIMDJSON_STRUCTURAL_INDEXER_STEP; + static constexpr const int STEP = SIMDJSON_STRUCTURAL_INDEXER_STEP; #else - static constexpr const int STEP = 4; + static constexpr const int STEP = 4; #endif - static constexpr const int STEP_UNTIL = 24; + static constexpr const int STEP_UNTIL = 24; - write_indexes_stepped<0, STEP_UNTIL, STEP>(idx, bits, cnt); - SIMDJSON_IF_CONSTEXPR(STEP_UNTIL < 64) { - if (simdjson_unlikely(STEP_UNTIL < cnt)) { - for (int i = STEP_UNTIL; i < cnt; i++) { - write_index(idx, bits, i); - } - } - } + write_indexes_stepped<0, STEP_UNTIL, STEP>(idx, bits, cnt); + SIMDJSON_IF_CONSTEXPR (STEP_UNTIL < 64) { + if (simdjson_unlikely(STEP_UNTIL < cnt)) { + for (int i=STEP_UNTIL; itail += cnt; - } + this->tail += cnt; + } #endif // SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER - }; +}; - class json_structural_indexer { - public: - /** - * Find the important bits of JSON in a 128-byte chunk, and add them to structural_indexes. - * - * @param partial Setting the partial parameter to true allows the find_structural_bits to - * tolerate unclosed strings. The caller should still ensure that the input is valid UTF-8. If - * you are processing substrings, you may want to call on a function like trimmed_length_safe_utf8. - */ - template - static error_code index(const uint8_t* buf, size_t len, dom_parser_implementation& parser, stage1_mode partial) noexcept; +class json_structural_indexer { +public: + /** + * Find the important bits of JSON in a 128-byte chunk, and add them to structural_indexes. + * + * @param partial Setting the partial parameter to true allows the find_structural_bits to + * tolerate unclosed strings. The caller should still ensure that the input is valid UTF-8. If + * you are processing substrings, you may want to call on a function like trimmed_length_safe_utf8. + */ + template + static error_code index(const uint8_t *buf, size_t len, dom_parser_implementation &parser, stage1_mode partial) noexcept; - private: - simdjson_inline json_structural_indexer(uint32_t* structural_indexes); - template - simdjson_inline void step(const uint8_t* block, buf_block_reader& reader) noexcept; - simdjson_inline void next(const simd::simd8x64& in, const json_block& block, size_t idx); - simdjson_inline error_code finish(dom_parser_implementation& parser, size_t idx, size_t len, stage1_mode partial); +private: + simdjson_inline json_structural_indexer(uint32_t *structural_indexes); + template + simdjson_inline void step(const uint8_t *block, buf_block_reader &reader) noexcept; + simdjson_inline void next(const simd::simd8x64& in, const json_block& block, size_t idx); + simdjson_warn_unused simdjson_inline error_code finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial); - json_scanner scanner{}; - utf8_checker checker{}; - bit_indexer indexer; - uint64_t prev_structurals = 0; - uint64_t unescaped_chars_error = 0; - }; + json_scanner scanner{}; + utf8_checker checker{}; + bit_indexer indexer; + uint64_t prev_structurals = 0; + uint64_t unescaped_chars_error = 0; +}; - simdjson_inline json_structural_indexer::json_structural_indexer(uint32_t* structural_indexes) : indexer{ structural_indexes } {} +simdjson_inline json_structural_indexer::json_structural_indexer(uint32_t *structural_indexes) : indexer{structural_indexes} {} - // Skip the last character if it is partial - simdjson_inline size_t trim_partial_utf8(const uint8_t* buf, size_t len) { - if (simdjson_unlikely(len < 3)) { - switch (len) { - case 2: - if (buf[len - 1] >= 0xc0) { return len - 1; } // 2-, 3- and 4-byte characters with only 1 byte left - if (buf[len - 2] >= 0xe0) { return len - 2; } // 3- and 4-byte characters with only 2 bytes left - return len; - case 1: - if (buf[len - 1] >= 0xc0) { return len - 1; } // 2-, 3- and 4-byte characters with only 1 byte left - return len; - case 0: - return len; - } - } - if (buf[len - 1] >= 0xc0) { return len - 1; } // 2-, 3- and 4-byte characters with only 1 byte left - if (buf[len - 2] >= 0xe0) { return len - 2; } // 3- and 4-byte characters with only 1 byte left - if (buf[len - 3] >= 0xf0) { return len - 3; } // 4-byte characters with only 3 bytes left - return len; - } +// Skip the last character if it is partial +simdjson_inline size_t trim_partial_utf8(const uint8_t *buf, size_t len) { + if (simdjson_unlikely(len < 3)) { + switch (len) { + case 2: + if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left + if (buf[len-2] >= 0xe0) { return len-2; } // 3- and 4-byte characters with only 2 bytes left + return len; + case 1: + if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left + return len; + case 0: + return len; + } + } + if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left + if (buf[len-2] >= 0xe0) { return len-2; } // 3- and 4-byte characters with only 1 byte left + if (buf[len-3] >= 0xf0) { return len-3; } // 4-byte characters with only 3 bytes left + return len; +} - // - // PERF NOTES: - // We pipe 2 inputs through these stages: - // 1. Load JSON into registers. This takes a long time and is highly parallelizable, so we load - // 2 inputs' worth at once so that by the time step 2 is looking for them input, it's available. - // 2. Scan the JSON for critical data: strings, scalars and operators. This is the critical path. - // The output of step 1 depends entirely on this information. These functions don't quite use - // up enough CPU: the second half of the functions is highly serial, only using 1 execution core - // at a time. The second input's scans has some dependency on the first ones finishing it, but - // they can make a lot of progress before they need that information. - // 3. Step 1 does not use enough capacity, so we run some extra stuff while we're waiting for that - // to finish: utf-8 checks and generating the output from the last iteration. - // - // The reason we run 2 inputs at a time, is steps 2 and 3 are *still* not enough to soak up all - // available capacity with just one input. Running 2 at a time seems to give the CPU a good enough - // workout. - // - template - error_code json_structural_indexer::index(const uint8_t* buf, size_t len, dom_parser_implementation& parser, stage1_mode partial) noexcept { - if (simdjson_unlikely(len > parser.capacity())) { return CAPACITY; } - // We guard the rest of the code so that we can assume that len > 0 throughout. - if (len == 0) { return EMPTY; } - if (is_streaming(partial)) { - len = trim_partial_utf8(buf, len); - // If you end up with an empty window after trimming - // the partial UTF-8 bytes, then chances are good that you - // have an UTF-8 formatting error. - if (len == 0) { return UTF8_ERROR; } - } - buf_block_reader reader(buf, len); - json_structural_indexer indexer(parser.structural_indexes.get()); +// +// PERF NOTES: +// We pipe 2 inputs through these stages: +// 1. Load JSON into registers. This takes a long time and is highly parallelizable, so we load +// 2 inputs' worth at once so that by the time step 2 is looking for them input, it's available. +// 2. Scan the JSON for critical data: strings, scalars and operators. This is the critical path. +// The output of step 1 depends entirely on this information. These functions don't quite use +// up enough CPU: the second half of the functions is highly serial, only using 1 execution core +// at a time. The second input's scans has some dependency on the first ones finishing it, but +// they can make a lot of progress before they need that information. +// 3. Step 1 does not use enough capacity, so we run some extra stuff while we're waiting for that +// to finish: utf-8 checks and generating the output from the last iteration. +// +// The reason we run 2 inputs at a time, is steps 2 and 3 are *still* not enough to soak up all +// available capacity with just one input. Running 2 at a time seems to give the CPU a good enough +// workout. +// +template +error_code json_structural_indexer::index(const uint8_t *buf, size_t len, dom_parser_implementation &parser, stage1_mode partial) noexcept { + if (simdjson_unlikely(len > parser.capacity())) { return CAPACITY; } + // We guard the rest of the code so that we can assume that len > 0 throughout. + if (len == 0) { return EMPTY; } + if (is_streaming(partial)) { + len = trim_partial_utf8(buf, len); + // If you end up with an empty window after trimming + // the partial UTF-8 bytes, then chances are good that you + // have an UTF-8 formatting error. + if(len == 0) { return UTF8_ERROR; } + } + buf_block_reader reader(buf, len); + json_structural_indexer indexer(parser.structural_indexes.get()); - // Read all but the last block - while (reader.has_full_block()) { - indexer.step(reader.full_block(), reader); - } - // Take care of the last block (will always be there unless file is empty which is - // not supposed to happen.) - uint8_t block[STEP_SIZE]; - if (simdjson_unlikely(reader.get_remainder(block) == 0)) { return UNEXPECTED_ERROR; } - indexer.step(block, reader); - return indexer.finish(parser, reader.block_index(), len, partial); - } + // Read all but the last block + while (reader.has_full_block()) { + indexer.step(reader.full_block(), reader); + } + // Take care of the last block (will always be there unless file is empty which is + // not supposed to happen.) + uint8_t block[STEP_SIZE]; + if (simdjson_unlikely(reader.get_remainder(block) == 0)) { return UNEXPECTED_ERROR; } + indexer.step(block, reader); + return indexer.finish(parser, reader.block_index(), len, partial); +} - template<> - simdjson_inline void json_structural_indexer::step<128>(const uint8_t* block, buf_block_reader<128>& reader) noexcept { - simd::simd8x64 in_1(block); - simd::simd8x64 in_2(block + 64); - json_block block_1 = scanner.next(in_1); - json_block block_2 = scanner.next(in_2); - this->next(in_1, block_1, reader.block_index()); - this->next(in_2, block_2, reader.block_index() + 64); - reader.advance(); - } +template<> +simdjson_inline void json_structural_indexer::step<128>(const uint8_t *block, buf_block_reader<128> &reader) noexcept { + simd::simd8x64 in_1(block); + simd::simd8x64 in_2(block+64); + json_block block_1 = scanner.next(in_1); + json_block block_2 = scanner.next(in_2); + this->next(in_1, block_1, reader.block_index()); + this->next(in_2, block_2, reader.block_index()+64); + reader.advance(); +} - template<> - simdjson_inline void json_structural_indexer::step<64>(const uint8_t* block, buf_block_reader<64>& reader) noexcept { - simd::simd8x64 in_1(block); - json_block block_1 = scanner.next(in_1); - this->next(in_1, block_1, reader.block_index()); - reader.advance(); - } +template<> +simdjson_inline void json_structural_indexer::step<64>(const uint8_t *block, buf_block_reader<64> &reader) noexcept { + simd::simd8x64 in_1(block); + json_block block_1 = scanner.next(in_1); + this->next(in_1, block_1, reader.block_index()); + reader.advance(); +} - simdjson_inline void json_structural_indexer::next(const simd::simd8x64& in, const json_block& block, size_t idx) { - uint64_t unescaped = in.lteq(0x1F); +simdjson_inline void json_structural_indexer::next(const simd::simd8x64& in, const json_block& block, size_t idx) { + uint64_t unescaped = in.lteq(0x1F); #if SIMDJSON_UTF8VALIDATION - checker.check_next_input(in); + checker.check_next_input(in); #endif - indexer.write(uint32_t(idx - 64), prev_structurals); // Output *last* iteration's structurals to the parser - prev_structurals = block.structural_start(); - unescaped_chars_error |= block.non_quote_inside_string(unescaped); - } + indexer.write(uint32_t(idx-64), prev_structurals); // Output *last* iteration's structurals to the parser + prev_structurals = block.structural_start(); + unescaped_chars_error |= block.non_quote_inside_string(unescaped); +} - simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementation& parser, size_t idx, size_t len, stage1_mode partial) { - // Write out the final iteration's structurals - indexer.write(uint32_t(idx - 64), prev_structurals); - error_code error = scanner.finish(); - // We deliberately break down the next expression so that it is - // human readable. - const bool should_we_exit = is_streaming(partial) ? - ((error != SUCCESS) && (error != UNCLOSED_STRING)) // when partial we tolerate UNCLOSED_STRING - : (error != SUCCESS); // if partial is false, we must have SUCCESS - const bool have_unclosed_string = (error == UNCLOSED_STRING); - if (simdjson_unlikely(should_we_exit)) { return error; } +simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial) { + // Write out the final iteration's structurals + indexer.write(uint32_t(idx-64), prev_structurals); + error_code error = scanner.finish(); + // We deliberately break down the next expression so that it is + // human readable. + const bool should_we_exit = is_streaming(partial) ? + ((error != SUCCESS) && (error != UNCLOSED_STRING)) // when partial we tolerate UNCLOSED_STRING + : (error != SUCCESS); // if partial is false, we must have SUCCESS + const bool have_unclosed_string = (error == UNCLOSED_STRING); + if (simdjson_unlikely(should_we_exit)) { return error; } - if (unescaped_chars_error) { - return UNESCAPED_CHARS; - } - parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get()); - /*** - * The On-Demand API requires special padding. - * - * This is related to https://github.com/simdjson/simdjson/issues/906 - * Basically, we want to make sure that if the parsing continues beyond the last (valid) - * structural character, it quickly stops. - * Only three structural characters can be repeated without triggering an error in JSON: [,] and }. - * We repeat the padding character (at 'len'). We don't know what it is, but if the parsing - * continues, then it must be [,] or }. - * Suppose it is ] or }. We backtrack to the first character, what could it be that would - * not trigger an error? It could be ] or } but no, because you can't start a document that way. - * It can't be a comma, a colon or any simple value. So the only way we could continue is - * if the repeated character is [. But if so, the document must start with [. But if the document - * starts with [, it should end with ]. If we enforce that rule, then we would get - * ][[ which is invalid. - * - * This is illustrated with the test array_iterate_unclosed_error() on the following input: - * R"({ "a": [,,)" - **/ - parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); // used later in partial == stage1_mode::streaming_final - parser.structural_indexes[parser.n_structural_indexes + 1] = uint32_t(len); - parser.structural_indexes[parser.n_structural_indexes + 2] = 0; - parser.next_structural_index = 0; - // a valid JSON file cannot have zero structural indexes - we should have found something - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { - return EMPTY; - } - if (simdjson_unlikely(parser.structural_indexes[parser.n_structural_indexes - 1] > len)) { - return UNEXPECTED_ERROR; - } - if (partial == stage1_mode::streaming_partial) { - // If we have an unclosed string, then the last structural - // will be the quote and we want to make sure to omit it. - if (have_unclosed_string) { - parser.n_structural_indexes--; - // a valid JSON file cannot have zero structural indexes - we should have found something - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; } - } - // We truncate the input to the end of the last complete document (or zero). - auto new_structural_indexes = find_next_document_index(parser); - if (new_structural_indexes == 0 && parser.n_structural_indexes > 0) { - if (parser.structural_indexes[0] == 0) { - // If the buffer is partial and we started at index 0 but the document is - // incomplete, it's too big to parse. - return CAPACITY; - } - else { - // It is possible that the document could be parsed, we just had a lot - // of white space. - parser.n_structural_indexes = 0; - return EMPTY; - } - } + if (unescaped_chars_error) { + return UNESCAPED_CHARS; + } + parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get()); + /*** + * The On-Demand API requires special padding. + * + * This is related to https://github.com/simdjson/simdjson/issues/906 + * Basically, we want to make sure that if the parsing continues beyond the last (valid) + * structural character, it quickly stops. + * Only three structural characters can be repeated without triggering an error in JSON: [,] and }. + * We repeat the padding character (at 'len'). We don't know what it is, but if the parsing + * continues, then it must be [,] or }. + * Suppose it is ] or }. We backtrack to the first character, what could it be that would + * not trigger an error? It could be ] or } but no, because you can't start a document that way. + * It can't be a comma, a colon or any simple value. So the only way we could continue is + * if the repeated character is [. But if so, the document must start with [. But if the document + * starts with [, it should end with ]. If we enforce that rule, then we would get + * ][[ which is invalid. + * + * This is illustrated with the test array_iterate_unclosed_error() on the following input: + * R"({ "a": [,,)" + **/ + parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); // used later in partial == stage1_mode::streaming_final + parser.structural_indexes[parser.n_structural_indexes + 1] = uint32_t(len); + parser.structural_indexes[parser.n_structural_indexes + 2] = 0; + parser.next_structural_index = 0; + // a valid JSON file cannot have zero structural indexes - we should have found something + if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { + return EMPTY; + } + if (simdjson_unlikely(parser.structural_indexes[parser.n_structural_indexes - 1] > len)) { + return UNEXPECTED_ERROR; + } + if (partial == stage1_mode::streaming_partial) { + // If we have an unclosed string, then the last structural + // will be the quote and we want to make sure to omit it. + if(have_unclosed_string) { + parser.n_structural_indexes--; + // a valid JSON file cannot have zero structural indexes - we should have found something + if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; } + } + // We truncate the input to the end of the last complete document (or zero). + auto new_structural_indexes = find_next_document_index(parser); + if (new_structural_indexes == 0 && parser.n_structural_indexes > 0) { + if(parser.structural_indexes[0] == 0) { + // If the buffer is partial and we started at index 0 but the document is + // incomplete, it's too big to parse. + return CAPACITY; + } else { + // It is possible that the document could be parsed, we just had a lot + // of white space. + parser.n_structural_indexes = 0; + return EMPTY; + } + } - parser.n_structural_indexes = new_structural_indexes; - } - else if (partial == stage1_mode::streaming_final) { - if (have_unclosed_string) { parser.n_structural_indexes--; } - // We truncate the input to the end of the last complete document (or zero). - // Because partial == stage1_mode::streaming_final, it means that we may - // silently ignore trailing garbage. Though it sounds bad, we do it - // deliberately because many people who have streams of JSON documents - // will truncate them for processing. E.g., imagine that you are uncompressing - // the data from a size file or receiving it in chunks from the network. You - // may not know where exactly the last document will be. Meanwhile the - // document_stream instances allow people to know the JSON documents they are - // parsing (see the iterator.source() method). - parser.n_structural_indexes = find_next_document_index(parser); - // We store the initial n_structural_indexes so that the client can see - // whether we used truncation. If initial_n_structural_indexes == parser.n_structural_indexes, - // then this will query parser.structural_indexes[parser.n_structural_indexes] which is len, - // otherwise, it will copy some prior index. - parser.structural_indexes[parser.n_structural_indexes + 1] = parser.structural_indexes[parser.n_structural_indexes]; - // This next line is critical, do not change it unless you understand what you are - // doing. - parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { - // We tolerate an unclosed string at the very end of the stream. Indeed, users - // often load their data in bulk without being careful and they want us to ignore - // the trailing garbage. - return EMPTY; - } - } - checker.check_eof(); - return checker.errors(); - } + parser.n_structural_indexes = new_structural_indexes; + } else if (partial == stage1_mode::streaming_final) { + if(have_unclosed_string) { parser.n_structural_indexes--; } + // We truncate the input to the end of the last complete document (or zero). + // Because partial == stage1_mode::streaming_final, it means that we may + // silently ignore trailing garbage. Though it sounds bad, we do it + // deliberately because many people who have streams of JSON documents + // will truncate them for processing. E.g., imagine that you are uncompressing + // the data from a size file or receiving it in chunks from the network. You + // may not know where exactly the last document will be. Meanwhile the + // document_stream instances allow people to know the JSON documents they are + // parsing (see the iterator.source() method). + parser.n_structural_indexes = find_next_document_index(parser); + // We store the initial n_structural_indexes so that the client can see + // whether we used truncation. If initial_n_structural_indexes == parser.n_structural_indexes, + // then this will query parser.structural_indexes[parser.n_structural_indexes] which is len, + // otherwise, it will copy some prior index. + parser.structural_indexes[parser.n_structural_indexes + 1] = parser.structural_indexes[parser.n_structural_indexes]; + // This next line is critical, do not change it unless you understand what you are + // doing. + parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); + if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { + // We tolerate an unclosed string at the very end of the stream. Indeed, users + // often load their data in bulk without being careful and they want us to ignore + // the trailing garbage. + return EMPTY; + } + } + checker.check_eof(); + return checker.errors(); +} - } // namespace stage1 - } // unnamed namespace - } // namespace haswell +} // namespace stage1 +} // unnamed namespace +} // namespace haswell } // namespace simdjson // Clear CUSTOM_BIT_INDEXER so other implementations can set it if they need to. @@ -18976,38 +20425,38 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace { - namespace stage1 { +namespace haswell { +namespace { +namespace stage1 { - /** - * Validates that the string is actual UTF-8. - */ - template - bool generic_validate_utf8(const uint8_t* input, size_t length) { - checker c{}; - buf_block_reader<64> reader(input, length); - while (reader.has_full_block()) { - simd::simd8x64 in(reader.full_block()); - c.check_next_input(in); - reader.advance(); - } - uint8_t block[64]{}; - reader.get_remainder(block); - simd::simd8x64 in(block); - c.check_next_input(in); - reader.advance(); - c.check_eof(); - return c.errors() == error_code::SUCCESS; - } +/** + * Validates that the string is actual UTF-8. + */ +template +bool generic_validate_utf8(const uint8_t * input, size_t length) { + checker c{}; + buf_block_reader<64> reader(input, length); + while (reader.has_full_block()) { + simd::simd8x64 in(reader.full_block()); + c.check_next_input(in); + reader.advance(); + } + uint8_t block[64]{}; + reader.get_remainder(block); + simd::simd8x64 in(block); + c.check_next_input(in); + reader.advance(); + c.check_eof(); + return c.errors() == error_code::SUCCESS; +} - bool generic_validate_utf8(const char* input, size_t length) { - return generic_validate_utf8(reinterpret_cast(input), length); - } +bool generic_validate_utf8(const char * input, size_t length) { + return generic_validate_utf8(reinterpret_cast(input),length); +} - } // namespace stage1 - } // unnamed namespace - } // namespace haswell +} // namespace stage1 +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_UTF8_VALIDATOR_H @@ -19026,18 +20475,18 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace { - namespace stage2 { +namespace haswell { +namespace { +namespace stage2 { - class json_iterator; - class structural_iterator; - struct tape_builder; - struct tape_writer; +class json_iterator; +class structural_iterator; +struct tape_builder; +struct tape_writer; - } // namespace stage2 - } // unnamed namespace - } // namespace haswell +} // namespace stage2 +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_BASE_H @@ -19055,109 +20504,109 @@ namespace simdjson { #include namespace simdjson { - namespace haswell { - namespace { - namespace stage2 { +namespace haswell { +namespace { +namespace stage2 { - struct tape_writer { - /** The next place to write to tape */ - uint64_t* next_tape_loc; +struct tape_writer { + /** The next place to write to tape */ + uint64_t *next_tape_loc; - /** Write a signed 64-bit value to tape. */ - simdjson_inline void append_s64(int64_t value) noexcept; + /** Write a signed 64-bit value to tape. */ + simdjson_inline void append_s64(int64_t value) noexcept; - /** Write an unsigned 64-bit value to tape. */ - simdjson_inline void append_u64(uint64_t value) noexcept; + /** Write an unsigned 64-bit value to tape. */ + simdjson_inline void append_u64(uint64_t value) noexcept; - /** Write a double value to tape. */ - simdjson_inline void append_double(double value) noexcept; + /** Write a double value to tape. */ + simdjson_inline void append_double(double value) noexcept; - /** - * Append a tape entry (an 8-bit type,and 56 bits worth of value). - */ - simdjson_inline void append(uint64_t val, internal::tape_type t) noexcept; + /** + * Append a tape entry (an 8-bit type,and 56 bits worth of value). + */ + simdjson_inline void append(uint64_t val, internal::tape_type t) noexcept; - /** - * Skip the current tape entry without writing. - * - * Used to skip the start of the container, since we'll come back later to fill it in when the - * container ends. - */ - simdjson_inline void skip() noexcept; + /** + * Skip the current tape entry without writing. + * + * Used to skip the start of the container, since we'll come back later to fill it in when the + * container ends. + */ + simdjson_inline void skip() noexcept; - /** - * Skip the number of tape entries necessary to write a large u64 or i64. - */ - simdjson_inline void skip_large_integer() noexcept; + /** + * Skip the number of tape entries necessary to write a large u64 or i64. + */ + simdjson_inline void skip_large_integer() noexcept; - /** - * Skip the number of tape entries necessary to write a double. - */ - simdjson_inline void skip_double() noexcept; + /** + * Skip the number of tape entries necessary to write a double. + */ + simdjson_inline void skip_double() noexcept; - /** - * Write a value to a known location on tape. - * - * Used to go back and write out the start of a container after the container ends. - */ - simdjson_inline static void write(uint64_t& tape_loc, uint64_t val, internal::tape_type t) noexcept; + /** + * Write a value to a known location on tape. + * + * Used to go back and write out the start of a container after the container ends. + */ + simdjson_inline static void write(uint64_t &tape_loc, uint64_t val, internal::tape_type t) noexcept; - private: - /** - * Append both the tape entry, and a supplementary value following it. Used for types that need - * all 64 bits, such as double and uint64_t. - */ - template - simdjson_inline void append2(uint64_t val, T val2, internal::tape_type t) noexcept; - }; // struct tape_writer +private: + /** + * Append both the tape entry, and a supplementary value following it. Used for types that need + * all 64 bits, such as double and uint64_t. + */ + template + simdjson_inline void append2(uint64_t val, T val2, internal::tape_type t) noexcept; +}; // struct tape_writer - simdjson_inline void tape_writer::append_s64(int64_t value) noexcept { - append2(0, value, internal::tape_type::INT64); - } +simdjson_inline void tape_writer::append_s64(int64_t value) noexcept { + append2(0, value, internal::tape_type::INT64); +} - simdjson_inline void tape_writer::append_u64(uint64_t value) noexcept { - append(0, internal::tape_type::UINT64); - *next_tape_loc = value; - next_tape_loc++; - } +simdjson_inline void tape_writer::append_u64(uint64_t value) noexcept { + append(0, internal::tape_type::UINT64); + *next_tape_loc = value; + next_tape_loc++; +} - /** Write a double value to tape. */ - simdjson_inline void tape_writer::append_double(double value) noexcept { - append2(0, value, internal::tape_type::DOUBLE); - } +/** Write a double value to tape. */ +simdjson_inline void tape_writer::append_double(double value) noexcept { + append2(0, value, internal::tape_type::DOUBLE); +} - simdjson_inline void tape_writer::skip() noexcept { - next_tape_loc++; - } +simdjson_inline void tape_writer::skip() noexcept { + next_tape_loc++; +} - simdjson_inline void tape_writer::skip_large_integer() noexcept { - next_tape_loc += 2; - } +simdjson_inline void tape_writer::skip_large_integer() noexcept { + next_tape_loc += 2; +} - simdjson_inline void tape_writer::skip_double() noexcept { - next_tape_loc += 2; - } +simdjson_inline void tape_writer::skip_double() noexcept { + next_tape_loc += 2; +} - simdjson_inline void tape_writer::append(uint64_t val, internal::tape_type t) noexcept { - *next_tape_loc = val | ((uint64_t(char(t))) << 56); - next_tape_loc++; - } +simdjson_inline void tape_writer::append(uint64_t val, internal::tape_type t) noexcept { + *next_tape_loc = val | ((uint64_t(char(t))) << 56); + next_tape_loc++; +} - template - simdjson_inline void tape_writer::append2(uint64_t val, T val2, internal::tape_type t) noexcept { - append(val, t); - static_assert(sizeof(val2) == sizeof(*next_tape_loc), "Type is not 64 bits!"); - memcpy(next_tape_loc, &val2, sizeof(val2)); - next_tape_loc++; - } +template +simdjson_inline void tape_writer::append2(uint64_t val, T val2, internal::tape_type t) noexcept { + append(val, t); + static_assert(sizeof(val2) == sizeof(*next_tape_loc), "Type is not 64 bits!"); + memcpy(next_tape_loc, &val2, sizeof(val2)); + next_tape_loc++; +} - simdjson_inline void tape_writer::write(uint64_t& tape_loc, uint64_t val, internal::tape_type t) noexcept { - tape_loc = val | ((uint64_t(char(t))) << 56); - } +simdjson_inline void tape_writer::write(uint64_t &tape_loc, uint64_t val, internal::tape_type t) noexcept { + tape_loc = val | ((uint64_t(char(t))) << 56); +} - } // namespace stage2 - } // unnamed namespace - } // namespace haswell +} // namespace stage2 +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_WRITER_H @@ -19177,92 +20626,90 @@ namespace simdjson { // This is for an internal-only stage 2 specific logger. // Set LOG_ENABLED = true to log what stage 2 is doing! namespace simdjson { - namespace haswell { - namespace { - namespace logger { +namespace haswell { +namespace { +namespace logger { - static constexpr const char* DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"; + static constexpr const char * DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"; #if SIMDJSON_VERBOSE_LOGGING - static constexpr const bool LOG_ENABLED = true; + static constexpr const bool LOG_ENABLED = true; #else - static constexpr const bool LOG_ENABLED = false; + static constexpr const bool LOG_ENABLED = false; #endif - 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 constexpr const int LOG_INDEX_LEN = 5; + 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 constexpr const int LOG_INDEX_LEN = 5; - static int log_depth; // Not threadsafe. Log only. + static int log_depth; // Not threadsafe. Log only. - // Helper to turn unprintable or newline characters into spaces - static simdjson_inline char printable_char(char c) { - if (c >= 0x20) { - return c; - } - else { - return ' '; - } - } + // Helper to turn unprintable or newline characters into spaces + static simdjson_inline char printable_char(char c) { + if (c >= 0x20) { + return c; + } else { + return ' '; + } + } - // Print the header and set up log_start - static simdjson_inline void log_start() { - if (LOG_ENABLED) { - log_depth = 0; - printf("\n"); - printf("| %-*s | %-*s | %-*s | %-*s | Detail |\n", LOG_EVENT_LEN, "Event", LOG_BUFFER_LEN, "Buffer", LOG_SMALL_BUFFER_LEN, "Next", 5, "Next#"); - printf("|%.*s|%.*s|%.*s|%.*s|--------|\n", LOG_EVENT_LEN + 2, DASHES, LOG_BUFFER_LEN + 2, DASHES, LOG_SMALL_BUFFER_LEN + 2, DASHES, 5 + 2, DASHES); - } - } + // Print the header and set up log_start + static simdjson_inline void log_start() { + if (LOG_ENABLED) { + log_depth = 0; + printf("\n"); + printf("| %-*s | %-*s | %-*s | %-*s | Detail |\n", LOG_EVENT_LEN, "Event", LOG_BUFFER_LEN, "Buffer", LOG_SMALL_BUFFER_LEN, "Next", 5, "Next#"); + printf("|%.*s|%.*s|%.*s|%.*s|--------|\n", LOG_EVENT_LEN+2, DASHES, LOG_BUFFER_LEN+2, DASHES, LOG_SMALL_BUFFER_LEN+2, DASHES, 5+2, DASHES); + } + } - simdjson_unused static simdjson_inline void log_string(const char* message) { - if (LOG_ENABLED) { - printf("%s\n", message); - } - } + simdjson_unused static simdjson_inline void log_string(const char *message) { + if (LOG_ENABLED) { + printf("%s\n", message); + } + } - // Logs a single line from the stage 2 DOM parser - template - static simdjson_inline void log_line(S& structurals, const char* title_prefix, const char* title, const char* detail) { - if (LOG_ENABLED) { - printf("| %*s%s%-*s ", log_depth * 2, "", title_prefix, LOG_EVENT_LEN - log_depth * 2 - int(strlen(title_prefix)), title); - auto current_index = structurals.at_beginning() ? nullptr : structurals.next_structural - 1; - auto next_index = structurals.next_structural; - auto current = current_index ? &structurals.buf[*current_index] : reinterpret_cast(" "); - auto next = &structurals.buf[*next_index]; - { - // Print the next N characters in the buffer. - printf("| "); - // Otherwise, print the characters starting from the buffer position. - // Print spaces for unprintable or newline characters. - for (int i = 0; i < LOG_BUFFER_LEN; i++) { - printf("%c", printable_char(current[i])); - } - printf(" "); - // Print the next N characters in the buffer. - printf("| "); - // Otherwise, print the characters starting from the buffer position. - // Print spaces for unprintable or newline characters. - for (int i = 0; i < LOG_SMALL_BUFFER_LEN; i++) { - printf("%c", printable_char(next[i])); - } - printf(" "); - } - if (current_index) { - printf("| %*u ", LOG_INDEX_LEN, *current_index); - } - else { - printf("| %-*s ", LOG_INDEX_LEN, ""); - } - // printf("| %*u ", LOG_INDEX_LEN, structurals.next_tape_index()); - printf("| %-s ", detail); - printf("|\n"); - } - } + // Logs a single line from the stage 2 DOM parser + template + static simdjson_inline void log_line(S &structurals, const char *title_prefix, const char *title, const char *detail) { + if (LOG_ENABLED) { + printf("| %*s%s%-*s ", log_depth*2, "", title_prefix, LOG_EVENT_LEN - log_depth*2 - int(strlen(title_prefix)), title); + auto current_index = structurals.at_beginning() ? nullptr : structurals.next_structural-1; + auto next_index = structurals.next_structural; + auto current = current_index ? &structurals.buf[*current_index] : reinterpret_cast(" "); + auto next = &structurals.buf[*next_index]; + { + // Print the next N characters in the buffer. + printf("| "); + // Otherwise, print the characters starting from the buffer position. + // Print spaces for unprintable or newline characters. + for (int i=0;i - simdjson_warn_unused simdjson_inline error_code walk_document(V& visitor) noexcept; + /** + * Walk the JSON document. + * + * The visitor receives callbacks when values are encountered. All callbacks pass the iterator as + * the first parameter; some callbacks have other parameters as well: + * + * - visit_document_start() - at the beginning. + * - visit_document_end() - at the end (if things were successful). + * + * - visit_array_start() - at the start `[` of a non-empty array. + * - visit_array_end() - at the end `]` of a non-empty array. + * - visit_empty_array() - when an empty array is encountered. + * + * - visit_object_end() - at the start `]` of a non-empty object. + * - visit_object_start() - at the end `]` of a non-empty object. + * - visit_empty_object() - when an empty object is encountered. + * - visit_key(const uint8_t *key) - when a key in an object field is encountered. key is + * guaranteed to point at the first quote of the string (`"key"`). + * - visit_primitive(const uint8_t *value) - when a value is a string, number, boolean or null. + * - visit_root_primitive(iter, uint8_t *value) - when the top-level value is a string, number, boolean or null. + * + * - increment_count(iter) - each time a value is found in an array or object. + */ + template + simdjson_warn_unused simdjson_inline error_code walk_document(V &visitor) noexcept; - /** - * Create an iterator capable of walking a JSON document. - * - * The document must have already passed through stage 1. - */ - simdjson_inline json_iterator(dom_parser_implementation& _dom_parser, size_t start_structural_index); + /** + * Create an iterator capable of walking a JSON document. + * + * The document must have already passed through stage 1. + */ + simdjson_inline json_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index); - /** - * Look at the next token. - * - * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). - * - * They may include invalid JSON as well (such as `1.2.3` or `ture`). - */ - simdjson_inline const uint8_t* peek() const noexcept; - /** - * Advance to the next token. - * - * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). - * - * They may include invalid JSON as well (such as `1.2.3` or `ture`). - */ - simdjson_inline const uint8_t* advance() noexcept; - /** - * Get the remaining length of the document, from the start of the current token. - */ - simdjson_inline size_t remaining_len() const noexcept; - /** - * Check if we are at the end of the document. - * - * If this is true, there are no more tokens. - */ - simdjson_inline bool at_eof() const noexcept; - /** - * Check if we are at the beginning of the document. - */ - simdjson_inline bool at_beginning() const noexcept; - simdjson_inline uint8_t last_structural() const noexcept; + /** + * Look at the next token. + * + * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). + * + * They may include invalid JSON as well (such as `1.2.3` or `ture`). + */ + simdjson_inline const uint8_t *peek() const noexcept; + /** + * Advance to the next token. + * + * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). + * + * They may include invalid JSON as well (such as `1.2.3` or `ture`). + */ + simdjson_inline const uint8_t *advance() noexcept; + /** + * Get the remaining length of the document, from the start of the current token. + */ + simdjson_inline size_t remaining_len() const noexcept; + /** + * Check if we are at the end of the document. + * + * If this is true, there are no more tokens. + */ + simdjson_inline bool at_eof() const noexcept; + /** + * Check if we are at the beginning of the document. + */ + simdjson_inline bool at_beginning() const noexcept; + simdjson_inline uint8_t last_structural() const noexcept; - /** - * Log that a value has been found. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_value(const char* type) const noexcept; - /** - * Log the start of a multipart value. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_start_value(const char* type) const noexcept; - /** - * Log the end of a multipart value. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_end_value(const char* type) const noexcept; - /** - * Log an error. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_error(const char* error) const noexcept; + /** + * Log that a value has been found. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_value(const char *type) const noexcept; + /** + * Log the start of a multipart value. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_start_value(const char *type) const noexcept; + /** + * Log the end of a multipart value. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_end_value(const char *type) const noexcept; + /** + * Log an error. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_error(const char *error) const noexcept; - template - simdjson_warn_unused simdjson_inline error_code visit_root_primitive(V& visitor, const uint8_t* value) noexcept; - template - simdjson_warn_unused simdjson_inline error_code visit_primitive(V& visitor, const uint8_t* value) noexcept; - }; + template + simdjson_warn_unused simdjson_inline error_code visit_root_primitive(V &visitor, const uint8_t *value) noexcept; + template + simdjson_warn_unused simdjson_inline error_code visit_primitive(V &visitor, const uint8_t *value) noexcept; +}; - template - simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V& visitor) noexcept { - logger::log_start(); +template +simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &visitor) noexcept { + logger::log_start(); - // - // Start the document - // - if (at_eof()) { return EMPTY; } - log_start_value("document"); - SIMDJSON_TRY(visitor.visit_document_start(*this)); + // + // Start the document + // + if (at_eof()) { return EMPTY; } + log_start_value("document"); + SIMDJSON_TRY( visitor.visit_document_start(*this) ); - // - // Read first value - // - { - auto value = advance(); + // + // Read first value + // + { + auto value = advance(); - // Make sure the outer object or array is closed before continuing; otherwise, there are ways we - // could get into memory corruption. See https://github.com/simdjson/simdjson/issues/906 - if (!STREAMING) { - switch (*value) { - case '{': if (last_structural() != '}') { log_value("starting brace unmatched"); return TAPE_ERROR; }; break; - case '[': if (last_structural() != ']') { log_value("starting bracket unmatched"); return TAPE_ERROR; }; break; - } - } + // Make sure the outer object or array is closed before continuing; otherwise, there are ways we + // could get into memory corruption. See https://github.com/simdjson/simdjson/issues/906 + if (!STREAMING) { + switch (*value) { + case '{': if (last_structural() != '}') { log_value("starting brace unmatched"); return TAPE_ERROR; }; break; + case '[': if (last_structural() != ']') { log_value("starting bracket unmatched"); return TAPE_ERROR; }; break; + } + } - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY(visitor.visit_empty_object(*this)); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY(visitor.visit_empty_array(*this)); break; } goto array_begin; - default: SIMDJSON_TRY(visitor.visit_root_primitive(*this, value)); break; - } - } - goto document_end; + switch (*value) { + case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; + case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; + default: SIMDJSON_TRY( visitor.visit_root_primitive(*this, value) ); break; + } + } + goto document_end; - // - // Object parser states - // - object_begin: - log_start_value("object"); - depth++; - if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } - dom_parser.is_array[depth] = false; - SIMDJSON_TRY(visitor.visit_object_start(*this)); +// +// Object parser states +// +object_begin: + log_start_value("object"); + depth++; + if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } + dom_parser.is_array[depth] = false; + SIMDJSON_TRY( visitor.visit_object_start(*this) ); - { - auto key = advance(); - if (*key != '"') { log_error("Object does not start with a key"); return TAPE_ERROR; } - SIMDJSON_TRY(visitor.increment_count(*this)); - SIMDJSON_TRY(visitor.visit_key(*this, key)); - } + { + auto key = advance(); + if (*key != '"') { log_error("Object does not start with a key"); return TAPE_ERROR; } + SIMDJSON_TRY( visitor.increment_count(*this) ); + SIMDJSON_TRY( visitor.visit_key(*this, key) ); + } - object_field: - if (simdjson_unlikely(*advance() != ':')) { log_error("Missing colon after key in object"); return TAPE_ERROR; } - { - auto value = advance(); - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY(visitor.visit_empty_object(*this)); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY(visitor.visit_empty_array(*this)); break; } goto array_begin; - default: SIMDJSON_TRY(visitor.visit_primitive(*this, value)); break; - } - } +object_field: + if (simdjson_unlikely( *advance() != ':' )) { log_error("Missing colon after key in object"); return TAPE_ERROR; } + { + auto value = advance(); + switch (*value) { + case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; + case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; + default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break; + } + } - object_continue: - switch (*advance()) { - case ',': - SIMDJSON_TRY(visitor.increment_count(*this)); - { - auto key = advance(); - if (simdjson_unlikely(*key != '"')) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; } - SIMDJSON_TRY(visitor.visit_key(*this, key)); - } - goto object_field; - case '}': log_end_value("object"); SIMDJSON_TRY(visitor.visit_object_end(*this)); goto scope_end; - default: log_error("No comma between object fields"); return TAPE_ERROR; - } +object_continue: + switch (*advance()) { + case ',': + SIMDJSON_TRY( visitor.increment_count(*this) ); + { + auto key = advance(); + if (simdjson_unlikely( *key != '"' )) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; } + SIMDJSON_TRY( visitor.visit_key(*this, key) ); + } + goto object_field; + case '}': log_end_value("object"); SIMDJSON_TRY( visitor.visit_object_end(*this) ); goto scope_end; + default: log_error("No comma between object fields"); return TAPE_ERROR; + } - scope_end: - depth--; - if (depth == 0) { goto document_end; } - if (dom_parser.is_array[depth]) { goto array_continue; } - goto object_continue; +scope_end: + depth--; + if (depth == 0) { goto document_end; } + if (dom_parser.is_array[depth]) { goto array_continue; } + goto object_continue; - // - // Array parser states - // - array_begin: - log_start_value("array"); - depth++; - if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } - dom_parser.is_array[depth] = true; - SIMDJSON_TRY(visitor.visit_array_start(*this)); - SIMDJSON_TRY(visitor.increment_count(*this)); +// +// Array parser states +// +array_begin: + log_start_value("array"); + depth++; + if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } + dom_parser.is_array[depth] = true; + SIMDJSON_TRY( visitor.visit_array_start(*this) ); + SIMDJSON_TRY( visitor.increment_count(*this) ); - array_value: - { - auto value = advance(); - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY(visitor.visit_empty_object(*this)); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY(visitor.visit_empty_array(*this)); break; } goto array_begin; - default: SIMDJSON_TRY(visitor.visit_primitive(*this, value)); break; - } - } +array_value: + { + auto value = advance(); + switch (*value) { + case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; + case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; + default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break; + } + } - array_continue: - switch (*advance()) { - case ',': SIMDJSON_TRY(visitor.increment_count(*this)); goto array_value; - case ']': log_end_value("array"); SIMDJSON_TRY(visitor.visit_array_end(*this)); goto scope_end; - default: log_error("Missing comma between array values"); return TAPE_ERROR; - } +array_continue: + switch (*advance()) { + case ',': SIMDJSON_TRY( visitor.increment_count(*this) ); goto array_value; + case ']': log_end_value("array"); SIMDJSON_TRY( visitor.visit_array_end(*this) ); goto scope_end; + default: log_error("Missing comma between array values"); return TAPE_ERROR; + } - document_end: - log_end_value("document"); - SIMDJSON_TRY(visitor.visit_document_end(*this)); +document_end: + log_end_value("document"); + SIMDJSON_TRY( visitor.visit_document_end(*this) ); - dom_parser.next_structural_index = uint32_t(next_structural - &dom_parser.structural_indexes[0]); + dom_parser.next_structural_index = uint32_t(next_structural - &dom_parser.structural_indexes[0]); - // If we didn't make it to the end, it's an error - if (!STREAMING && dom_parser.next_structural_index != dom_parser.n_structural_indexes) { - log_error("More than one JSON value at the root of the document, or extra characters at the end of the JSON!"); - return TAPE_ERROR; - } + // If we didn't make it to the end, it's an error + if ( !STREAMING && dom_parser.next_structural_index != dom_parser.n_structural_indexes ) { + log_error("More than one JSON value at the root of the document, or extra characters at the end of the JSON!"); + return TAPE_ERROR; + } - return SUCCESS; + return SUCCESS; - } // walk_document() +} // walk_document() - simdjson_inline json_iterator::json_iterator(dom_parser_implementation& _dom_parser, size_t start_structural_index) - : buf{ _dom_parser.buf }, - next_structural{ &_dom_parser.structural_indexes[start_structural_index] }, - dom_parser{ _dom_parser } { - } +simdjson_inline json_iterator::json_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index) + : buf{_dom_parser.buf}, + next_structural{&_dom_parser.structural_indexes[start_structural_index]}, + dom_parser{_dom_parser} { +} - simdjson_inline const uint8_t* json_iterator::peek() const noexcept { - return &buf[*(next_structural)]; - } - simdjson_inline const uint8_t* json_iterator::advance() noexcept { - return &buf[*(next_structural++)]; - } - simdjson_inline size_t json_iterator::remaining_len() const noexcept { - return dom_parser.len - *(next_structural - 1); - } +simdjson_inline const uint8_t *json_iterator::peek() const noexcept { + return &buf[*(next_structural)]; +} +simdjson_inline const uint8_t *json_iterator::advance() noexcept { + return &buf[*(next_structural++)]; +} +simdjson_inline size_t json_iterator::remaining_len() const noexcept { + return dom_parser.len - *(next_structural-1); +} - simdjson_inline bool json_iterator::at_eof() const noexcept { - return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; - } - simdjson_inline bool json_iterator::at_beginning() const noexcept { - return next_structural == dom_parser.structural_indexes.get(); - } - simdjson_inline uint8_t json_iterator::last_structural() const noexcept { - return buf[dom_parser.structural_indexes[dom_parser.n_structural_indexes - 1]]; - } +simdjson_inline bool json_iterator::at_eof() const noexcept { + return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; +} +simdjson_inline bool json_iterator::at_beginning() const noexcept { + return next_structural == dom_parser.structural_indexes.get(); +} +simdjson_inline uint8_t json_iterator::last_structural() const noexcept { + return buf[dom_parser.structural_indexes[dom_parser.n_structural_indexes - 1]]; +} - simdjson_inline void json_iterator::log_value(const char* type) const noexcept { - logger::log_line(*this, "", type, ""); - } +simdjson_inline void json_iterator::log_value(const char *type) const noexcept { + logger::log_line(*this, "", type, ""); +} - simdjson_inline void json_iterator::log_start_value(const char* type) const noexcept { - logger::log_line(*this, "+", type, ""); - if (logger::LOG_ENABLED) { logger::log_depth++; } - } +simdjson_inline void json_iterator::log_start_value(const char *type) const noexcept { + logger::log_line(*this, "+", type, ""); + if (logger::LOG_ENABLED) { logger::log_depth++; } +} - simdjson_inline void json_iterator::log_end_value(const char* type) const noexcept { - if (logger::LOG_ENABLED) { logger::log_depth--; } - logger::log_line(*this, "-", type, ""); - } +simdjson_inline void json_iterator::log_end_value(const char *type) const noexcept { + if (logger::LOG_ENABLED) { logger::log_depth--; } + logger::log_line(*this, "-", type, ""); +} - simdjson_inline void json_iterator::log_error(const char* error) const noexcept { - logger::log_line(*this, "", "ERROR", error); - } +simdjson_inline void json_iterator::log_error(const char *error) const noexcept { + logger::log_line(*this, "", "ERROR", error); +} - template - simdjson_warn_unused simdjson_inline error_code json_iterator::visit_root_primitive(V& visitor, const uint8_t* value) noexcept { - switch (*value) { - case '"': return visitor.visit_root_string(*this, value); - case 't': return visitor.visit_root_true_atom(*this, value); - case 'f': return visitor.visit_root_false_atom(*this, value); - case 'n': return visitor.visit_root_null_atom(*this, value); - case '-': - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - return visitor.visit_root_number(*this, value); - default: - log_error("Document starts with a non-value character"); - return TAPE_ERROR; - } - } - template - simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V& visitor, const uint8_t* value) noexcept { - // Use the fact that most scalars are going to be either strings or numbers. - if (*value == '"') { - return visitor.visit_string(*this, value); - } - else if (((*value - '0') < 10) || (*value == '-')) { - return visitor.visit_number(*this, value); - } - // true, false, null are uncommon. - switch (*value) { - case 't': return visitor.visit_true_atom(*this, value); - case 'f': return visitor.visit_false_atom(*this, value); - case 'n': return visitor.visit_null_atom(*this, value); - default: - log_error("Non-value found when value was expected!"); - return TAPE_ERROR; - } - } +template +simdjson_warn_unused simdjson_inline error_code json_iterator::visit_root_primitive(V &visitor, const uint8_t *value) noexcept { + switch (*value) { + case '"': return visitor.visit_root_string(*this, value); + case 't': return visitor.visit_root_true_atom(*this, value); + case 'f': return visitor.visit_root_false_atom(*this, value); + case 'n': return visitor.visit_root_null_atom(*this, value); + case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return visitor.visit_root_number(*this, value); + default: + log_error("Document starts with a non-value character"); + return TAPE_ERROR; + } +} +template +simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V &visitor, const uint8_t *value) noexcept { + // Use the fact that most scalars are going to be either strings or numbers. + if(*value == '"') { + return visitor.visit_string(*this, value); + } else if (((*value - '0') < 10) || (*value == '-')) { + return visitor.visit_number(*this, value); + } + // true, false, null are uncommon. + switch (*value) { + case 't': return visitor.visit_true_atom(*this, value); + case 'f': return visitor.visit_false_atom(*this, value); + case 'n': return visitor.visit_null_atom(*this, value); + default: + log_error("Non-value found when value was expected!"); + return TAPE_ERROR; + } +} - } // namespace stage2 - } // unnamed namespace - } // namespace haswell +} // namespace stage2 +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_JSON_ITERATOR_H /* end file generic/stage2/json_iterator.h for haswell */ /* including generic/stage2/stringparsing.h for haswell: #include */ /* begin file generic/stage2/stringparsing.h for haswell */ +#include #ifndef SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -19615,242 +21062,238 @@ namespace simdjson { // It is intended to be included multiple times and compiled multiple times namespace simdjson { - namespace haswell { - namespace { - /// @private - namespace stringparsing { +namespace haswell { +namespace { +/// @private +namespace stringparsing { - // begin copypasta - // These chars yield themselves: " \ / - // b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab - // u not handled in this table as it's complex - static const uint8_t escape_map[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// begin copypasta +// These chars yield themselves: " \ / +// b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab +// u not handled in this table as it's complex +static const uint8_t escape_map[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0. + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5. - 0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6. - 0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7. + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4. + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5. + 0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6. + 0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - }; + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; - // handle a unicode codepoint - // write appropriate values into dest - // src will advance 6 bytes or 12 bytes - // dest will advance a variable amount (return via pointer) - // return true if the unicode codepoint was valid - // We work in little-endian then swap at write time - simdjson_warn_unused - simdjson_inline bool handle_unicode_codepoint(const uint8_t** src_ptr, - uint8_t** dst_ptr, bool allow_replacement) { - // Use the default Unicode Character 'REPLACEMENT CHARACTER' (U+FFFD) - constexpr uint32_t substitution_code_point = 0xfffd; - // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the - // conversion is not valid; we defer the check for this to inside the - // multilingual plane check. - uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); - *src_ptr += 6; +// handle a unicode codepoint +// write appropriate values into dest +// src will advance 6 bytes or 12 bytes +// dest will advance a variable amount (return via pointer) +// return true if the unicode codepoint was valid +// We work in little-endian then swap at write time +simdjson_warn_unused +simdjson_inline bool handle_unicode_codepoint(const uint8_t **src_ptr, + uint8_t **dst_ptr, bool allow_replacement) { + // Use the default Unicode Character 'REPLACEMENT CHARACTER' (U+FFFD) + constexpr uint32_t substitution_code_point = 0xfffd; + // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the + // conversion is not valid; we defer the check for this to inside the + // multilingual plane check. + uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); + *src_ptr += 6; - // If we found a high surrogate, we must - // check for low surrogate for characters - // outside the Basic - // Multilingual Plane. - if (code_point >= 0xd800 && code_point < 0xdc00) { - const uint8_t* src_data = *src_ptr; - /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ - if (((src_data[0] << 8) | src_data[1]) != ((static_cast ('\\') << 8) | static_cast ('u'))) { - if (!allow_replacement) { return false; } - code_point = substitution_code_point; - } - else { - uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); + // If we found a high surrogate, we must + // check for low surrogate for characters + // outside the Basic + // Multilingual Plane. + if (code_point >= 0xd800 && code_point < 0xdc00) { + const uint8_t *src_data = *src_ptr; + /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ + if (((src_data[0] << 8) | src_data[1]) != ((static_cast ('\\') << 8) | static_cast ('u'))) { + if(!allow_replacement) { return false; } + code_point = substitution_code_point; + } else { + uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); - // We have already checked that the high surrogate is valid and - // (code_point - 0xd800) < 1024. - // - // Check that code_point_2 is in the range 0xdc00..0xdfff - // and that code_point_2 was parsed from valid hex. - uint32_t low_bit = code_point_2 - 0xdc00; - if (low_bit >> 10) { - if (!allow_replacement) { return false; } - code_point = substitution_code_point; - } - else { - code_point = (((code_point - 0xd800) << 10) | low_bit) + 0x10000; - *src_ptr += 6; - } + // We have already checked that the high surrogate is valid and + // (code_point - 0xd800) < 1024. + // + // Check that code_point_2 is in the range 0xdc00..0xdfff + // and that code_point_2 was parsed from valid hex. + uint32_t low_bit = code_point_2 - 0xdc00; + if (low_bit >> 10) { + if(!allow_replacement) { return false; } + code_point = substitution_code_point; + } else { + code_point = (((code_point - 0xd800) << 10) | low_bit) + 0x10000; + *src_ptr += 6; + } - } - } - else if (code_point >= 0xdc00 && code_point <= 0xdfff) { - // If we encounter a low surrogate (not preceded by a high surrogate) - // then we have an error. - if (!allow_replacement) { return false; } - code_point = substitution_code_point; - } - size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); - *dst_ptr += offset; - return offset > 0; - } + } + } else if (code_point >= 0xdc00 && code_point <= 0xdfff) { + // If we encounter a low surrogate (not preceded by a high surrogate) + // then we have an error. + if(!allow_replacement) { return false; } + code_point = substitution_code_point; + } + size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); + *dst_ptr += offset; + return offset > 0; +} - // handle a unicode codepoint using the wobbly convention - // https://simonsapin.github.io/wtf-8/ - // write appropriate values into dest - // src will advance 6 bytes or 12 bytes - // dest will advance a variable amount (return via pointer) - // return true if the unicode codepoint was valid - // We work in little-endian then swap at write time - simdjson_warn_unused - simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t** src_ptr, - uint8_t** dst_ptr) { - // It is not ideal that this function is nearly identical to handle_unicode_codepoint. - // - // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the - // conversion is not valid; we defer the check for this to inside the - // multilingual plane check. - uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); - *src_ptr += 6; - // If we found a high surrogate, we must - // check for low surrogate for characters - // outside the Basic - // Multilingual Plane. - if (code_point >= 0xd800 && code_point < 0xdc00) { - const uint8_t* src_data = *src_ptr; - /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ - if (((src_data[0] << 8) | src_data[1]) == ((static_cast ('\\') << 8) | static_cast ('u'))) { - uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); - uint32_t low_bit = code_point_2 - 0xdc00; - if ((low_bit >> 10) == 0) { - code_point = - (((code_point - 0xd800) << 10) | low_bit) + 0x10000; - *src_ptr += 6; - } - } - } +// handle a unicode codepoint using the wobbly convention +// https://simonsapin.github.io/wtf-8/ +// write appropriate values into dest +// src will advance 6 bytes or 12 bytes +// dest will advance a variable amount (return via pointer) +// return true if the unicode codepoint was valid +// We work in little-endian then swap at write time +simdjson_warn_unused +simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t **src_ptr, + uint8_t **dst_ptr) { + // It is not ideal that this function is nearly identical to handle_unicode_codepoint. + // + // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the + // conversion is not valid; we defer the check for this to inside the + // multilingual plane check. + uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); + *src_ptr += 6; + // If we found a high surrogate, we must + // check for low surrogate for characters + // outside the Basic + // Multilingual Plane. + if (code_point >= 0xd800 && code_point < 0xdc00) { + const uint8_t *src_data = *src_ptr; + /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ + if (((src_data[0] << 8) | src_data[1]) == ((static_cast ('\\') << 8) | static_cast ('u'))) { + uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); + uint32_t low_bit = code_point_2 - 0xdc00; + if ((low_bit >> 10) == 0) { + code_point = + (((code_point - 0xd800) << 10) | low_bit) + 0x10000; + *src_ptr += 6; + } + } + } - size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); - *dst_ptr += offset; - return offset > 0; - } + size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); + *dst_ptr += offset; + return offset > 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. - */ - simdjson_warn_unused simdjson_inline uint8_t* parse_string(const uint8_t* src, uint8_t* dst, bool allow_replacement) { - while (1) { - // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); - // If the next thing is the end quote, copy and return - if (bs_quote.has_quote_first()) { - // we encountered quotes first. Move dst to point to quotes and exit - return dst + bs_quote.quote_index(); - } - if (bs_quote.has_backslash()) { - /* find out where the backspace is */ - auto bs_dist = bs_quote.backslash_index(); - uint8_t escape_char = src[bs_dist + 1]; - /* we encountered backslash first. Handle backslash */ - if (escape_char == 'u') { - /* move src/dst up to the start; they will be further adjusted - within the unicode codepoint handling code. */ - src += bs_dist; - dst += bs_dist; - if (!handle_unicode_codepoint(&src, &dst, allow_replacement)) { - return nullptr; - } - } - else { - /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and - * write bs_dist+1 characters to output - * note this may reach beyond the part of the buffer we've actually - * seen. I think this is ok */ - uint8_t escape_result = escape_map[escape_char]; - if (escape_result == 0u) { - return nullptr; /* bogus escape value is an error */ - } - dst[bs_dist] = escape_result; - src += bs_dist + 2; - dst += bs_dist + 1; - } - } - else { - /* they are the same. Since they can't co-occur, it means we - * encountered neither. */ - src += backslash_and_quote::BYTES_PROCESSED; - dst += backslash_and_quote::BYTES_PROCESSED; - } - } - } +/** + * 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. + */ +simdjson_warn_unused simdjson_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) { + while (1) { + // Copy the next n bytes, and find the backslash and quote in them. + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); + // If the next thing is the end quote, copy and return + if (bs_quote.has_quote_first()) { + // we encountered quotes first. Move dst to point to quotes and exit + return dst + bs_quote.quote_index(); + } + if (bs_quote.has_backslash()) { + /* find out where the backspace is */ + auto bs_dist = bs_quote.backslash_index(); + uint8_t escape_char = src[bs_dist + 1]; + /* we encountered backslash first. Handle backslash */ + if (escape_char == 'u') { + /* move src/dst up to the start; they will be further adjusted + within the unicode codepoint handling code. */ + src += bs_dist; + dst += bs_dist; + if (!handle_unicode_codepoint(&src, &dst, allow_replacement)) { + return nullptr; + } + } else { + /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and + * write bs_dist+1 characters to output + * note this may reach beyond the part of the buffer we've actually + * seen. I think this is ok */ + uint8_t escape_result = escape_map[escape_char]; + if (escape_result == 0u) { + return nullptr; /* bogus escape value is an error */ + } + dst[bs_dist] = escape_result; + src += bs_dist + 2; + dst += bs_dist + 1; + } + } else { + /* they are the same. Since they can't co-occur, it means we + * encountered neither. */ + src += backslash_and_quote::BYTES_PROCESSED; + dst += backslash_and_quote::BYTES_PROCESSED; + } + } +} - simdjson_warn_unused simdjson_inline uint8_t* parse_wobbly_string(const uint8_t* src, uint8_t* dst) { - // It is not ideal that this function is nearly identical to parse_string. - while (1) { - // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); - // If the next thing is the end quote, copy and return - if (bs_quote.has_quote_first()) { - // we encountered quotes first. Move dst to point to quotes and exit - return dst + bs_quote.quote_index(); - } - if (bs_quote.has_backslash()) { - /* find out where the backspace is */ - auto bs_dist = bs_quote.backslash_index(); - uint8_t escape_char = src[bs_dist + 1]; - /* we encountered backslash first. Handle backslash */ - if (escape_char == 'u') { - /* move src/dst up to the start; they will be further adjusted - within the unicode codepoint handling code. */ - src += bs_dist; - dst += bs_dist; - if (!handle_unicode_codepoint_wobbly(&src, &dst)) { - return nullptr; - } - } - else { - /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and - * write bs_dist+1 characters to output - * note this may reach beyond the part of the buffer we've actually - * seen. I think this is ok */ - uint8_t escape_result = escape_map[escape_char]; - if (escape_result == 0u) { - return nullptr; /* bogus escape value is an error */ - } - dst[bs_dist] = escape_result; - src += bs_dist + 2; - dst += bs_dist + 1; - } - } - else { - /* they are the same. Since they can't co-occur, it means we - * encountered neither. */ - src += backslash_and_quote::BYTES_PROCESSED; - dst += backslash_and_quote::BYTES_PROCESSED; - } - } - } +simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) { + // It is not ideal that this function is nearly identical to parse_string. + while (1) { + // Copy the next n bytes, and find the backslash and quote in them. + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); + // If the next thing is the end quote, copy and return + if (bs_quote.has_quote_first()) { + // we encountered quotes first. Move dst to point to quotes and exit + return dst + bs_quote.quote_index(); + } + if (bs_quote.has_backslash()) { + /* find out where the backspace is */ + auto bs_dist = bs_quote.backslash_index(); + uint8_t escape_char = src[bs_dist + 1]; + /* we encountered backslash first. Handle backslash */ + if (escape_char == 'u') { + /* move src/dst up to the start; they will be further adjusted + within the unicode codepoint handling code. */ + src += bs_dist; + dst += bs_dist; + if (!handle_unicode_codepoint_wobbly(&src, &dst)) { + return nullptr; + } + } else { + /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and + * write bs_dist+1 characters to output + * note this may reach beyond the part of the buffer we've actually + * seen. I think this is ok */ + uint8_t escape_result = escape_map[escape_char]; + if (escape_result == 0u) { + return nullptr; /* bogus escape value is an error */ + } + dst[bs_dist] = escape_result; + src += bs_dist + 2; + dst += bs_dist + 1; + } + } else { + /* they are the same. Since they can't co-occur, it means we + * encountered neither. */ + src += backslash_and_quote::BYTES_PROCESSED; + dst += backslash_and_quote::BYTES_PROCESSED; + } + } +} - } // namespace stringparsing - } // unnamed namespace - } // namespace haswell +} // namespace stringparsing + +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H @@ -19866,58 +21309,58 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace { - namespace stage2 { +namespace haswell { +namespace { +namespace stage2 { - class structural_iterator { - public: - const uint8_t* const buf; - uint32_t* next_structural; - dom_parser_implementation& dom_parser; +class structural_iterator { +public: + const uint8_t* const buf; + uint32_t *next_structural; + dom_parser_implementation &dom_parser; - // Start a structural - simdjson_inline structural_iterator(dom_parser_implementation& _dom_parser, size_t start_structural_index) - : buf{ _dom_parser.buf }, - next_structural{ &_dom_parser.structural_indexes[start_structural_index] }, - dom_parser{ _dom_parser } { - } - // Get the buffer position of the current structural character - simdjson_inline const uint8_t* current() { - return &buf[*(next_structural - 1)]; - } - // Get the current structural character - simdjson_inline char current_char() { - return buf[*(next_structural - 1)]; - } - // Get the next structural character without advancing - simdjson_inline char peek_next_char() { - return buf[*next_structural]; - } - simdjson_inline const uint8_t* peek() { - return &buf[*next_structural]; - } - simdjson_inline const uint8_t* advance() { - return &buf[*(next_structural++)]; - } - simdjson_inline char advance_char() { - return buf[*(next_structural++)]; - } - simdjson_inline size_t remaining_len() { - return dom_parser.len - *(next_structural - 1); - } + // Start a structural + simdjson_inline structural_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index) + : buf{_dom_parser.buf}, + next_structural{&_dom_parser.structural_indexes[start_structural_index]}, + dom_parser{_dom_parser} { + } + // Get the buffer position of the current structural character + simdjson_inline const uint8_t* current() { + return &buf[*(next_structural-1)]; + } + // Get the current structural character + simdjson_inline char current_char() { + return buf[*(next_structural-1)]; + } + // Get the next structural character without advancing + simdjson_inline char peek_next_char() { + return buf[*next_structural]; + } + simdjson_inline const uint8_t* peek() { + return &buf[*next_structural]; + } + simdjson_inline const uint8_t* advance() { + return &buf[*(next_structural++)]; + } + simdjson_inline char advance_char() { + return buf[*(next_structural++)]; + } + simdjson_inline size_t remaining_len() { + return dom_parser.len - *(next_structural-1); + } - simdjson_inline bool at_end() { - return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; - } - simdjson_inline bool at_beginning() { - return next_structural == dom_parser.structural_indexes.get(); - } - }; + simdjson_inline bool at_end() { + return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; + } + simdjson_inline bool at_beginning() { + return next_structural == dom_parser.structural_indexes.get(); + } +}; - } // namespace stage2 - } // unnamed namespace - } // namespace haswell +} // namespace stage2 +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_STRUCTURAL_ITERATOR_H @@ -19940,284 +21383,284 @@ namespace simdjson { namespace simdjson { - namespace haswell { - namespace { - namespace stage2 { +namespace haswell { +namespace { +namespace stage2 { - struct tape_builder { - template - simdjson_warn_unused static simdjson_inline error_code parse_document( - dom_parser_implementation& dom_parser, - dom::document& doc) noexcept; +struct tape_builder { + template + simdjson_warn_unused static simdjson_inline error_code parse_document( + dom_parser_implementation &dom_parser, + dom::document &doc) noexcept; - /** Called when a non-empty document starts. */ - simdjson_warn_unused simdjson_inline error_code visit_document_start(json_iterator& iter) noexcept; - /** Called when a non-empty document ends without error. */ - simdjson_warn_unused simdjson_inline error_code visit_document_end(json_iterator& iter) noexcept; + /** Called when a non-empty document starts. */ + simdjson_warn_unused simdjson_inline error_code visit_document_start(json_iterator &iter) noexcept; + /** Called when a non-empty document ends without error. */ + simdjson_warn_unused simdjson_inline error_code visit_document_end(json_iterator &iter) noexcept; - /** Called when a non-empty array starts. */ - simdjson_warn_unused simdjson_inline error_code visit_array_start(json_iterator& iter) noexcept; - /** Called when a non-empty array ends. */ - simdjson_warn_unused simdjson_inline error_code visit_array_end(json_iterator& iter) noexcept; - /** Called when an empty array is found. */ - simdjson_warn_unused simdjson_inline error_code visit_empty_array(json_iterator& iter) noexcept; + /** Called when a non-empty array starts. */ + simdjson_warn_unused simdjson_inline error_code visit_array_start(json_iterator &iter) noexcept; + /** Called when a non-empty array ends. */ + simdjson_warn_unused simdjson_inline error_code visit_array_end(json_iterator &iter) noexcept; + /** Called when an empty array is found. */ + simdjson_warn_unused simdjson_inline error_code visit_empty_array(json_iterator &iter) noexcept; - /** Called when a non-empty object starts. */ - simdjson_warn_unused simdjson_inline error_code visit_object_start(json_iterator& iter) noexcept; - /** - * Called when a key in a field is encountered. - * - * primitive, visit_object_start, visit_empty_object, visit_array_start, or visit_empty_array - * will be called after this with the field value. - */ - simdjson_warn_unused simdjson_inline error_code visit_key(json_iterator& iter, const uint8_t* key) noexcept; - /** Called when a non-empty object ends. */ - simdjson_warn_unused simdjson_inline error_code visit_object_end(json_iterator& iter) noexcept; - /** Called when an empty object is found. */ - simdjson_warn_unused simdjson_inline error_code visit_empty_object(json_iterator& iter) noexcept; + /** Called when a non-empty object starts. */ + simdjson_warn_unused simdjson_inline error_code visit_object_start(json_iterator &iter) noexcept; + /** + * Called when a key in a field is encountered. + * + * primitive, visit_object_start, visit_empty_object, visit_array_start, or visit_empty_array + * will be called after this with the field value. + */ + simdjson_warn_unused simdjson_inline error_code visit_key(json_iterator &iter, const uint8_t *key) noexcept; + /** Called when a non-empty object ends. */ + simdjson_warn_unused simdjson_inline error_code visit_object_end(json_iterator &iter) noexcept; + /** Called when an empty object is found. */ + simdjson_warn_unused simdjson_inline error_code visit_empty_object(json_iterator &iter) noexcept; - /** - * Called when a string, number, boolean or null is found. - */ - simdjson_warn_unused simdjson_inline error_code visit_primitive(json_iterator& iter, const uint8_t* value) noexcept; - /** - * Called when a string, number, boolean or null is found at the top level of a document (i.e. - * when there is no array or object and the entire document is a single string, number, boolean or - * null. - * - * This is separate from primitive() because simdjson's normal primitive parsing routines assume - * there is at least one more token after the value, which is only true in an array or object. - */ - simdjson_warn_unused simdjson_inline error_code visit_root_primitive(json_iterator& iter, const uint8_t* value) noexcept; + /** + * Called when a string, number, boolean or null is found. + */ + simdjson_warn_unused simdjson_inline error_code visit_primitive(json_iterator &iter, const uint8_t *value) noexcept; + /** + * Called when a string, number, boolean or null is found at the top level of a document (i.e. + * when there is no array or object and the entire document is a single string, number, boolean or + * null. + * + * This is separate from primitive() because simdjson's normal primitive parsing routines assume + * there is at least one more token after the value, which is only true in an array or object. + */ + simdjson_warn_unused simdjson_inline error_code visit_root_primitive(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_string(json_iterator& iter, const uint8_t* value, bool key = false) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_number(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_true_atom(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_false_atom(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_null_atom(json_iterator& iter, const uint8_t* value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_string(json_iterator &iter, const uint8_t *value, bool key = false) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_number(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_true_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_false_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_null_atom(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_string(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_number(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_true_atom(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_false_atom(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_null_atom(json_iterator& iter, const uint8_t* value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_string(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_number(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_true_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept; - /** Called each time a new field or element in an array or object is found. */ - simdjson_warn_unused simdjson_inline error_code increment_count(json_iterator& iter) noexcept; + /** Called each time a new field or element in an array or object is found. */ + simdjson_warn_unused simdjson_inline error_code increment_count(json_iterator &iter) noexcept; - /** Next location to write to tape */ - tape_writer tape; - private: - /** Next write location in the string buf for stage 2 parsing */ - uint8_t* current_string_buf_loc; + /** Next location to write to tape */ + tape_writer tape; +private: + /** Next write location in the string buf for stage 2 parsing */ + uint8_t *current_string_buf_loc; - simdjson_inline tape_builder(dom::document& doc) noexcept; + simdjson_inline tape_builder(dom::document &doc) noexcept; - simdjson_inline uint32_t next_tape_index(json_iterator& iter) const noexcept; - simdjson_inline void start_container(json_iterator& iter) noexcept; - simdjson_warn_unused simdjson_inline error_code end_container(json_iterator& iter, internal::tape_type start, internal::tape_type end) noexcept; - simdjson_warn_unused simdjson_inline error_code empty_container(json_iterator& iter, internal::tape_type start, internal::tape_type end) noexcept; - simdjson_inline uint8_t* on_start_string(json_iterator& iter) noexcept; - simdjson_inline void on_end_string(uint8_t* dst) noexcept; - }; // struct tape_builder + simdjson_inline uint32_t next_tape_index(json_iterator &iter) const noexcept; + simdjson_inline void start_container(json_iterator &iter) noexcept; + simdjson_warn_unused simdjson_inline error_code end_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept; + simdjson_warn_unused simdjson_inline error_code empty_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept; + simdjson_inline uint8_t *on_start_string(json_iterator &iter) noexcept; + simdjson_inline void on_end_string(uint8_t *dst) noexcept; +}; // struct tape_builder - template - simdjson_warn_unused simdjson_inline error_code tape_builder::parse_document( - dom_parser_implementation& dom_parser, - dom::document& doc) noexcept { - dom_parser.doc = &doc; - json_iterator iter(dom_parser, STREAMING ? dom_parser.next_structural_index : 0); - tape_builder builder(doc); - return iter.walk_document(builder); - } +template +simdjson_warn_unused simdjson_inline error_code tape_builder::parse_document( + dom_parser_implementation &dom_parser, + dom::document &doc) noexcept { + dom_parser.doc = &doc; + json_iterator iter(dom_parser, STREAMING ? dom_parser.next_structural_index : 0); + tape_builder builder(doc); + return iter.walk_document(builder); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_primitive(json_iterator& iter, const uint8_t* value) noexcept { - return iter.visit_root_primitive(*this, value); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_primitive(json_iterator& iter, const uint8_t* value) noexcept { - return iter.visit_primitive(*this, value); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_object(json_iterator& iter) noexcept { - return empty_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_array(json_iterator& iter) noexcept { - return empty_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_primitive(json_iterator &iter, const uint8_t *value) noexcept { + return iter.visit_root_primitive(*this, value); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_primitive(json_iterator &iter, const uint8_t *value) noexcept { + return iter.visit_primitive(*this, value); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_object(json_iterator &iter) noexcept { + return empty_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_array(json_iterator &iter) noexcept { + return empty_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_start(json_iterator& iter) noexcept { - start_container(iter); - return SUCCESS; - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_start(json_iterator& iter) noexcept { - start_container(iter); - return SUCCESS; - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_start(json_iterator& iter) noexcept { - start_container(iter); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_start(json_iterator &iter) noexcept { + start_container(iter); + return SUCCESS; +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_start(json_iterator &iter) noexcept { + start_container(iter); + return SUCCESS; +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_start(json_iterator &iter) noexcept { + start_container(iter); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_end(json_iterator& iter) noexcept { - return end_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_end(json_iterator& iter) noexcept { - return end_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_end(json_iterator& iter) noexcept { - constexpr uint32_t start_tape_index = 0; - tape.append(start_tape_index, internal::tape_type::ROOT); - tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter), internal::tape_type::ROOT); - return SUCCESS; - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_key(json_iterator& iter, const uint8_t* key) noexcept { - return visit_string(iter, key, true); - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_end(json_iterator &iter) noexcept { + return end_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_end(json_iterator &iter) noexcept { + return end_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_end(json_iterator &iter) noexcept { + constexpr uint32_t start_tape_index = 0; + tape.append(start_tape_index, internal::tape_type::ROOT); + tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter), internal::tape_type::ROOT); + return SUCCESS; +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_key(json_iterator &iter, const uint8_t *key) noexcept { + return visit_string(iter, key, true); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::increment_count(json_iterator& iter) noexcept { - iter.dom_parser.open_containers[iter.depth].count++; // we have a key value pair in the object at parser.dom_parser.depth - 1 - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::increment_count(json_iterator &iter) noexcept { + iter.dom_parser.open_containers[iter.depth].count++; // we have a key value pair in the object at parser.dom_parser.depth - 1 + return SUCCESS; +} - simdjson_inline tape_builder::tape_builder(dom::document& doc) noexcept : tape{ doc.tape.get() }, current_string_buf_loc{ doc.string_buf.get() } {} +simdjson_inline tape_builder::tape_builder(dom::document &doc) noexcept : tape{doc.tape.get()}, current_string_buf_loc{doc.string_buf.get()} {} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_string(json_iterator& iter, const uint8_t* value, bool key) noexcept { - iter.log_value(key ? "key" : "string"); - uint8_t* dst = on_start_string(iter); - dst = stringparsing::parse_string(value + 1, dst, false); // We do not allow replacement when the escape characters are invalid. - if (dst == nullptr) { - iter.log_error("Invalid escape in string"); - return STRING_ERROR; - } - on_end_string(dst); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_string(json_iterator &iter, const uint8_t *value, bool key) noexcept { + iter.log_value(key ? "key" : "string"); + uint8_t *dst = on_start_string(iter); + dst = stringparsing::parse_string(value+1, dst, false); // We do not allow replacement when the escape characters are invalid. + if (dst == nullptr) { + iter.log_error("Invalid escape in string"); + return STRING_ERROR; + } + on_end_string(dst); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_string(json_iterator& iter, const uint8_t* value) noexcept { - return visit_string(iter, value); - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_string(json_iterator &iter, const uint8_t *value) noexcept { + return visit_string(iter, value); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_number(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("number"); - return numberparsing::parse_number(value, tape); - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_number(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("number"); + return numberparsing::parse_number(value, tape); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_number(json_iterator& iter, const uint8_t* value) noexcept { - // - // We need to make a copy to make sure that the string is space terminated. - // This is not about padding the input, which should already padded up - // to len + SIMDJSON_PADDING. However, we have no control at this stage - // on how the padding was done. What if the input string was padded with nulls? - // It is quite common for an input string to have an extra null character (C string). - // We do not want to allow 9\0 (where \0 is the null character) inside a JSON - // document, but the string "9\0" by itself is fine. So we make a copy and - // pad the input with spaces when we know that there is just one input element. - // This copy is relatively expensive, but it will almost never be called in - // practice unless you are in the strange scenario where you have many JSON - // documents made of single atoms. - // - std::unique_ptrcopy(new (std::nothrow) uint8_t[iter.remaining_len() + SIMDJSON_PADDING]); - if (copy.get() == nullptr) { return MEMALLOC; } - std::memcpy(copy.get(), value, iter.remaining_len()); - std::memset(copy.get() + iter.remaining_len(), ' ', SIMDJSON_PADDING); - error_code error = visit_number(iter, copy.get()); - return error; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_number(json_iterator &iter, const uint8_t *value) noexcept { + // + // We need to make a copy to make sure that the string is space terminated. + // This is not about padding the input, which should already padded up + // to len + SIMDJSON_PADDING. However, we have no control at this stage + // on how the padding was done. What if the input string was padded with nulls? + // It is quite common for an input string to have an extra null character (C string). + // We do not want to allow 9\0 (where \0 is the null character) inside a JSON + // document, but the string "9\0" by itself is fine. So we make a copy and + // pad the input with spaces when we know that there is just one input element. + // This copy is relatively expensive, but it will almost never be called in + // practice unless you are in the strange scenario where you have many JSON + // documents made of single atoms. + // + std::unique_ptrcopy(new (std::nothrow) uint8_t[iter.remaining_len() + SIMDJSON_PADDING]); + if (copy.get() == nullptr) { return MEMALLOC; } + std::memcpy(copy.get(), value, iter.remaining_len()); + std::memset(copy.get() + iter.remaining_len(), ' ', SIMDJSON_PADDING); + error_code error = visit_number(iter, copy.get()); + return error; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_true_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("true"); - if (!atomparsing::is_valid_true_atom(value)) { return T_ATOM_ERROR; } - tape.append(0, internal::tape_type::TRUE_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_true_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("true"); + if (!atomparsing::is_valid_true_atom(value)) { return T_ATOM_ERROR; } + tape.append(0, internal::tape_type::TRUE_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_true_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("true"); - if (!atomparsing::is_valid_true_atom(value, iter.remaining_len())) { return T_ATOM_ERROR; } - tape.append(0, internal::tape_type::TRUE_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_true_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("true"); + if (!atomparsing::is_valid_true_atom(value, iter.remaining_len())) { return T_ATOM_ERROR; } + tape.append(0, internal::tape_type::TRUE_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_false_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("false"); - if (!atomparsing::is_valid_false_atom(value)) { return F_ATOM_ERROR; } - tape.append(0, internal::tape_type::FALSE_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_false_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("false"); + if (!atomparsing::is_valid_false_atom(value)) { return F_ATOM_ERROR; } + tape.append(0, internal::tape_type::FALSE_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_false_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("false"); - if (!atomparsing::is_valid_false_atom(value, iter.remaining_len())) { return F_ATOM_ERROR; } - tape.append(0, internal::tape_type::FALSE_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("false"); + if (!atomparsing::is_valid_false_atom(value, iter.remaining_len())) { return F_ATOM_ERROR; } + tape.append(0, internal::tape_type::FALSE_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_null_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("null"); - if (!atomparsing::is_valid_null_atom(value)) { return N_ATOM_ERROR; } - tape.append(0, internal::tape_type::NULL_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_null_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("null"); + if (!atomparsing::is_valid_null_atom(value)) { return N_ATOM_ERROR; } + tape.append(0, internal::tape_type::NULL_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_null_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("null"); - if (!atomparsing::is_valid_null_atom(value, iter.remaining_len())) { return N_ATOM_ERROR; } - tape.append(0, internal::tape_type::NULL_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("null"); + if (!atomparsing::is_valid_null_atom(value, iter.remaining_len())) { return N_ATOM_ERROR; } + tape.append(0, internal::tape_type::NULL_VALUE); + return SUCCESS; +} - // private: +// private: - simdjson_inline uint32_t tape_builder::next_tape_index(json_iterator& iter) const noexcept { - return uint32_t(tape.next_tape_loc - iter.dom_parser.doc->tape.get()); - } +simdjson_inline uint32_t tape_builder::next_tape_index(json_iterator &iter) const noexcept { + return uint32_t(tape.next_tape_loc - iter.dom_parser.doc->tape.get()); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::empty_container(json_iterator& iter, internal::tape_type start, internal::tape_type end) noexcept { - auto start_index = next_tape_index(iter); - tape.append(start_index + 2, start); - tape.append(start_index, end); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::empty_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept { + auto start_index = next_tape_index(iter); + tape.append(start_index+2, start); + tape.append(start_index, end); + return SUCCESS; +} - simdjson_inline void tape_builder::start_container(json_iterator& iter) noexcept { - iter.dom_parser.open_containers[iter.depth].tape_index = next_tape_index(iter); - iter.dom_parser.open_containers[iter.depth].count = 0; - tape.skip(); // We don't actually *write* the start element until the end. - } +simdjson_inline void tape_builder::start_container(json_iterator &iter) noexcept { + iter.dom_parser.open_containers[iter.depth].tape_index = next_tape_index(iter); + iter.dom_parser.open_containers[iter.depth].count = 0; + tape.skip(); // We don't actually *write* the start element until the end. +} - simdjson_warn_unused simdjson_inline error_code tape_builder::end_container(json_iterator& iter, internal::tape_type start, internal::tape_type end) noexcept { - // Write the ending tape element, pointing at the start location - const uint32_t start_tape_index = iter.dom_parser.open_containers[iter.depth].tape_index; - tape.append(start_tape_index, end); - // Write the start tape element, pointing at the end location (and including count) - // count can overflow if it exceeds 24 bits... so we saturate - // the convention being that a cnt of 0xffffff or more is undetermined in value (>= 0xffffff). - const uint32_t count = iter.dom_parser.open_containers[iter.depth].count; - const uint32_t cntsat = count > 0xFFFFFF ? 0xFFFFFF : count; - tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter) | (uint64_t(cntsat) << 32), start); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::end_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept { + // Write the ending tape element, pointing at the start location + const uint32_t start_tape_index = iter.dom_parser.open_containers[iter.depth].tape_index; + tape.append(start_tape_index, end); + // Write the start tape element, pointing at the end location (and including count) + // count can overflow if it exceeds 24 bits... so we saturate + // the convention being that a cnt of 0xffffff or more is undetermined in value (>= 0xffffff). + const uint32_t count = iter.dom_parser.open_containers[iter.depth].count; + const uint32_t cntsat = count > 0xFFFFFF ? 0xFFFFFF : count; + tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter) | (uint64_t(cntsat) << 32), start); + return SUCCESS; +} - simdjson_inline uint8_t* tape_builder::on_start_string(json_iterator& iter) noexcept { - // we advance the point, accounting for the fact that we have a NULL termination - tape.append(current_string_buf_loc - iter.dom_parser.doc->string_buf.get(), internal::tape_type::STRING); - return current_string_buf_loc + sizeof(uint32_t); - } +simdjson_inline uint8_t *tape_builder::on_start_string(json_iterator &iter) noexcept { + // we advance the point, accounting for the fact that we have a NULL termination + tape.append(current_string_buf_loc - iter.dom_parser.doc->string_buf.get(), internal::tape_type::STRING); + return current_string_buf_loc + sizeof(uint32_t); +} - simdjson_inline void tape_builder::on_end_string(uint8_t* dst) noexcept { - uint32_t str_length = uint32_t(dst - (current_string_buf_loc + sizeof(uint32_t))); - // TODO check for overflow in case someone has a crazy string (>=4GB?) - // But only add the overflow check when the document itself exceeds 4GB - // Currently unneeded because we refuse to parse docs larger or equal to 4GB. - memcpy(current_string_buf_loc, &str_length, sizeof(uint32_t)); - // NULL termination is still handy if you expect all your strings to - // be NULL terminated? It comes at a small cost - *dst = 0; - current_string_buf_loc = dst + 1; - } +simdjson_inline void tape_builder::on_end_string(uint8_t *dst) noexcept { + uint32_t str_length = uint32_t(dst - (current_string_buf_loc + sizeof(uint32_t))); + // TODO check for overflow in case someone has a crazy string (>=4GB?) + // But only add the overflow check when the document itself exceeds 4GB + // Currently unneeded because we refuse to parse docs larger or equal to 4GB. + memcpy(current_string_buf_loc, &str_length, sizeof(uint32_t)); + // NULL termination is still handy if you expect all your strings to + // be NULL terminated? It comes at a small cost + *dst = 0; + current_string_buf_loc = dst + 1; +} - } // namespace stage2 - } // unnamed namespace - } // namespace haswell +} // namespace stage2 +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_BUILDER_H @@ -20229,101 +21672,101 @@ namespace simdjson { // namespace simdjson { - namespace haswell { +namespace haswell { - simdjson_warn_unused error_code implementation::create_dom_parser_implementation( - size_t capacity, - size_t max_depth, - std::unique_ptr& dst - ) const noexcept { - dst.reset(new (std::nothrow) dom_parser_implementation()); - if (!dst) { return MEMALLOC; } - if (auto err = dst->set_capacity(capacity)) - return err; - if (auto err = dst->set_max_depth(max_depth)) - return err; - return SUCCESS; - } +simdjson_warn_unused error_code implementation::create_dom_parser_implementation( + size_t capacity, + size_t max_depth, + std::unique_ptr& dst +) const noexcept { + dst.reset( new (std::nothrow) dom_parser_implementation() ); + if (!dst) { return MEMALLOC; } + if (auto err = dst->set_capacity(capacity)) + return err; + if (auto err = dst->set_max_depth(max_depth)) + return err; + return SUCCESS; +} - namespace { +namespace { - using namespace simd; +using namespace simd; - // This identifies structural characters (comma, colon, braces, brackets), - // and ASCII white-space ('\r','\n','\t',' '). - simdjson_inline json_character_block json_character_block::classify(const simd::simd8x64& in) { - // These lookups rely on the fact that anything < 127 will match the lower 4 bits, which is why - // we can't use the generic lookup_16. - const auto whitespace_table = simd8::repeat_16(' ', 100, 100, 100, 17, 100, 113, 2, 100, '\t', '\n', 112, 100, '\r', 100, 100); +// This identifies structural characters (comma, colon, braces, brackets), +// and ASCII white-space ('\r','\n','\t',' '). +simdjson_inline json_character_block json_character_block::classify(const simd::simd8x64& in) { + // These lookups rely on the fact that anything < 127 will match the lower 4 bits, which is why + // we can't use the generic lookup_16. + const auto whitespace_table = simd8::repeat_16(' ', 100, 100, 100, 17, 100, 113, 2, 100, '\t', '\n', 112, 100, '\r', 100, 100); - // The 6 operators (:,[]{}) have these values: - // - // , 2C - // : 3A - // [ 5B - // { 7B - // ] 5D - // } 7D - // - // If you use | 0x20 to turn [ and ] into { and }, the lower 4 bits of each character is unique. - // We exploit this, using a simd 4-bit lookup to tell us which character match against, and then - // match it (against | 0x20). - // - // To prevent recognizing other characters, everything else gets compared with 0, which cannot - // match due to the | 0x20. - // - // NOTE: Due to the | 0x20, this ALSO treats and (control characters 0C and 1A) like , - // and :. This gets caught in stage 2, which checks the actual character to ensure the right - // operators are in the right places. - const auto op_table = simd8::repeat_16( - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, ':', '{', // : = 3A, [ = 5B, { = 7B - ',', '}', 0, 0 // , = 2C, ] = 5D, } = 7D - ); + // The 6 operators (:,[]{}) have these values: + // + // , 2C + // : 3A + // [ 5B + // { 7B + // ] 5D + // } 7D + // + // If you use | 0x20 to turn [ and ] into { and }, the lower 4 bits of each character is unique. + // We exploit this, using a simd 4-bit lookup to tell us which character match against, and then + // match it (against | 0x20). + // + // To prevent recognizing other characters, everything else gets compared with 0, which cannot + // match due to the | 0x20. + // + // NOTE: Due to the | 0x20, this ALSO treats and (control characters 0C and 1A) like , + // and :. This gets caught in stage 2, which checks the actual character to ensure the right + // operators are in the right places. + const auto op_table = simd8::repeat_16( + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, ':', '{', // : = 3A, [ = 5B, { = 7B + ',', '}', 0, 0 // , = 2C, ] = 5D, } = 7D + ); - // We compute whitespace and op separately. If later code only uses one or the - // other, given the fact that all functions are aggressively inlined, we can - // hope that useless computations will be omitted. This is namely case when - // minifying (we only need whitespace). + // We compute whitespace and op separately. If later code only uses one or the + // other, given the fact that all functions are aggressively inlined, we can + // hope that useless computations will be omitted. This is namely case when + // minifying (we only need whitespace). - const uint64_t whitespace = in.eq({ - _mm256_shuffle_epi8(whitespace_table, in.chunks[0]), - _mm256_shuffle_epi8(whitespace_table, in.chunks[1]) - }); - // Turn [ and ] into { and } - const simd8x64 curlified{ - in.chunks[0] | 0x20, - in.chunks[1] | 0x20 - }; - const uint64_t op = curlified.eq({ - _mm256_shuffle_epi8(op_table, in.chunks[0]), - _mm256_shuffle_epi8(op_table, in.chunks[1]) - }); + const uint64_t whitespace = in.eq({ + _mm256_shuffle_epi8(whitespace_table, in.chunks[0]), + _mm256_shuffle_epi8(whitespace_table, in.chunks[1]) + }); + // Turn [ and ] into { and } + const simd8x64 curlified{ + in.chunks[0] | 0x20, + in.chunks[1] | 0x20 + }; + const uint64_t op = curlified.eq({ + _mm256_shuffle_epi8(op_table, in.chunks[0]), + _mm256_shuffle_epi8(op_table, in.chunks[1]) + }); - return { whitespace, op }; - } + return { whitespace, op }; +} - simdjson_inline bool is_ascii(const simd8x64& input) { - return input.reduce_or().is_ascii(); - } +simdjson_inline bool is_ascii(const simd8x64& input) { + return input.reduce_or().is_ascii(); +} - simdjson_unused simdjson_inline simd8 must_be_continuation(const simd8 prev1, const simd8 prev2, const simd8 prev3) { - simd8 is_second_byte = prev1.saturating_sub(0xc0u - 1); // Only 11______ will be > 0 - simd8 is_third_byte = prev2.saturating_sub(0xe0u - 1); // Only 111_____ will be > 0 - simd8 is_fourth_byte = prev3.saturating_sub(0xf0u - 1); // Only 1111____ will be > 0 - // Caller requires a bool (all 1's). All values resulting from the subtraction will be <= 64, so signed comparison is fine. - return simd8(is_second_byte | is_third_byte | is_fourth_byte) > int8_t(0); - } +simdjson_unused simdjson_inline simd8 must_be_continuation(const simd8 prev1, const simd8 prev2, const simd8 prev3) { + simd8 is_second_byte = prev1.saturating_sub(0xc0u-1); // Only 11______ will be > 0 + simd8 is_third_byte = prev2.saturating_sub(0xe0u-1); // Only 111_____ will be > 0 + simd8 is_fourth_byte = prev3.saturating_sub(0xf0u-1); // Only 1111____ will be > 0 + // Caller requires a bool (all 1's). All values resulting from the subtraction will be <= 64, so signed comparison is fine. + return simd8(is_second_byte | is_third_byte | is_fourth_byte) > int8_t(0); +} - simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { - simd8 is_third_byte = prev2.saturating_sub(0xe0u - 0x80); // Only 111_____ will be >= 0x80 - simd8 is_fourth_byte = prev3.saturating_sub(0xf0u - 0x80); // Only 1111____ will be >= 0x80 - return is_third_byte | is_fourth_byte; - } +simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { + simd8 is_third_byte = prev2.saturating_sub(0xe0u-0x80); // Only 111_____ will be >= 0x80 + simd8 is_fourth_byte = prev3.saturating_sub(0xf0u-0x80); // Only 1111____ will be >= 0x80 + return is_third_byte | is_fourth_byte; +} - } // unnamed namespace - } // namespace haswell +} // unnamed namespace +} // namespace haswell } // namespace simdjson // @@ -20334,45 +21777,46 @@ namespace simdjson { // Implementation-specific overrides // namespace simdjson { - namespace haswell { +namespace haswell { - simdjson_warn_unused error_code implementation::minify(const uint8_t* buf, size_t len, uint8_t* dst, size_t& dst_len) const noexcept { - return haswell::stage1::json_minifier::minify<128>(buf, len, dst, dst_len); - } +simdjson_warn_unused error_code implementation::minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept { + return haswell::stage1::json_minifier::minify<128>(buf, len, dst, dst_len); +} - simdjson_warn_unused error_code dom_parser_implementation::stage1(const uint8_t* _buf, size_t _len, stage1_mode streaming) noexcept { - this->buf = _buf; - this->len = _len; - return haswell::stage1::json_structural_indexer::index<128>(_buf, _len, *this, streaming); - } +simdjson_warn_unused error_code dom_parser_implementation::stage1(const uint8_t *_buf, size_t _len, stage1_mode streaming) noexcept { + this->buf = _buf; + this->len = _len; + return haswell::stage1::json_structural_indexer::index<128>(_buf, _len, *this, streaming); +} - simdjson_warn_unused bool implementation::validate_utf8(const char* buf, size_t len) const noexcept { - return haswell::stage1::generic_validate_utf8(buf, len); - } +simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept { + return haswell::stage1::generic_validate_utf8(buf,len); +} - simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document& _doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); - } +simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept { + return stage2::tape_builder::parse_document(*this, _doc); +} - simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document& _doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); - } +simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept { + return stage2::tape_builder::parse_document(*this, _doc); +} - simdjson_warn_unused uint8_t* dom_parser_implementation::parse_string(const uint8_t* src, uint8_t* dst, bool replacement_char) const noexcept { - return haswell::stringparsing::parse_string(src, dst, replacement_char); - } +SIMDJSON_NO_SANITIZE_MEMORY +simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool replacement_char) const noexcept { + return haswell::stringparsing::parse_string(src, dst, replacement_char); +} - simdjson_warn_unused uint8_t* dom_parser_implementation::parse_wobbly_string(const uint8_t* src, uint8_t* dst) const noexcept { - return haswell::stringparsing::parse_wobbly_string(src, dst); - } +simdjson_warn_unused uint8_t *dom_parser_implementation::parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept { + return haswell::stringparsing::parse_wobbly_string(src, dst); +} - simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t* _buf, size_t _len, dom::document& _doc) noexcept { - auto error = stage1(_buf, _len, stage1_mode::regular); - if (error) { return error; } - return stage2(_doc); - } +simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept { + auto error = stage1(_buf, _len, stage1_mode::regular); + if (error) { return error; } + return stage2(_doc); +} - } // namespace haswell +} // namespace haswell } // namespace simdjson /* including simdjson/haswell/end.h: #include */ @@ -20422,14 +21866,14 @@ SIMDJSON_UNTARGET_REGION // 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 { +/** + * Implementation for Icelake (Intel AVX512). + */ +namespace icelake { - class implementation; +class implementation; - } // namespace icelake +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_ICELAKE_BASE_H @@ -20477,7 +21921,7 @@ namespace simdjson { #include #include #include // for _mm_clmulepi64_si128 - // Important: we need the AVX-512 headers: +// Important: we need the AVX-512 headers: #include #include #include @@ -20513,64 +21957,64 @@ SIMDJSON_TARGET_REGION("avx512f,avx512dq,avx512cd,avx512bw,avx512vbmi,avx512vbmi /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { - namespace { +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) { +// 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); + 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); + //////// + // 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 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)); - } +/* 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 - } +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); - } +simdjson_inline long long int count_ones(uint64_t input_num) { + return _popcnt64(input_num); +} #endif - simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, - uint64_t* result) { +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { #if SIMDJSON_REGULAR_VISUAL_STUDIO - return _addcarry_u64(0, value1, value2, - reinterpret_cast(result)); + return _addcarry_u64(0, value1, value2, + reinterpret_cast(result)); #else - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); #endif - } +} - } // unnamed namespace - } // namespace icelake +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_ICELAKE_BITMANIPULATION_H @@ -20586,24 +22030,24 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { - namespace { +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); - } +// +// 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 +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_ICELAKE_BITMASK_H @@ -20631,354 +22075,358 @@ namespace simdjson { * 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)); + 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 { +namespace icelake { +namespace { +namespace simd { - // Forward-declared so they can be used by splat and friends. - template - struct base { - __m512i value; + // Forward-declared so they can be used by splat and friends. + template + struct base { + __m512i value; - // Zero constructor - simdjson_inline base() : value{ __m512i() } {} + // Zero constructor + simdjson_inline base() : value{__m512i()} {} - // Conversion from SIMD register - simdjson_inline base(const __m512i _value) : value(_value) {} + // 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; } + // 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(this); *this_cast = *this_cast | other; return *this_cast; } - simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } - simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } - }; + // 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(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; - // Forward-declared so they can be used by splat and friends. - template - struct simd8; + // Forward-declared so they can be used by splat and friends. + template + struct simd8; - template> - struct base8 : base> { - typedef uint32_t bitmask_t; - typedef uint64_t bitmask2_t; + template> + struct base8: base> { + typedef uint32_t bitmask_t; + typedef uint64_t bitmask2_t; - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m512i _value) : base>(_value) {} + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m512i _value) : base>(_value) {} - friend simdjson_really_inline uint64_t operator==(const simd8 lhs, const simd8 rhs) { - return _mm512_cmpeq_epi8_mask(lhs, rhs); - } + friend simdjson_really_inline uint64_t operator==(const simd8 lhs, const simd8 rhs) { + return _mm512_cmpeq_epi8_mask(lhs, rhs); + } + static const int SIZE = sizeof(base::value); - static const int SIZE = sizeof(base::value); + template + simdjson_inline simd8 prev(const simd8 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); + } + }; - template - simdjson_inline simd8 prev(const simd8 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: base8 { + static simdjson_inline simd8 splat(bool _value) { return _mm512_set1_epi8(uint8_t(-(!!_value))); } - // SIMD byte mask type (returned by things like eq and gt) - template<> - struct simd8 : base8 { - static simdjson_inline simd8 splat(bool _value) { return _mm512_set1_epi8(uint8_t(-(!!_value))); } + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m512i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(splat(_value)) {} + simdjson_inline bool any() const { return !!_mm512_test_epi8_mask (*this, *this); } + simdjson_inline simd8 operator~() const { return *this ^ true; } + }; - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m512i _value) : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) : base8(splat(_value)) {} - simdjson_inline bool any() const { return !!_mm512_test_epi8_mask(*this, *this); } - simdjson_inline simd8 operator~() const { return *this ^ true; } - }; + template + struct base8_numeric: base8 { + static simdjson_inline simd8 splat(T _value) { return _mm512_set1_epi8(_value); } + static simdjson_inline simd8 zero() { return _mm512_setzero_si512(); } + static simdjson_inline simd8 load(const T values[64]) { + return _mm512_loadu_si512(reinterpret_cast(values)); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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( + 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 + ); + } - template - struct base8_numeric : base8 { - static simdjson_inline simd8 splat(T _value) { return _mm512_set1_epi8(_value); } - static simdjson_inline simd8 zero() { return _mm512_setzero_si512(); } - static simdjson_inline simd8 load(const T values[64]) { - return _mm512_loadu_si512(reinterpret_cast(values)); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdjson_inline simd8 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( - 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() {} + simdjson_inline base8_numeric(const __m512i _value) : base8(_value) {} - simdjson_inline base8_numeric() : base8() {} - simdjson_inline base8_numeric(const __m512i _value) : base8(_value) {} + // Store to array + simdjson_inline void store(T dst[64]) const { return _mm512_storeu_si512(reinterpret_cast<__m512i *>(dst), *this); } - // 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 operator+(const simd8 other) const { return _mm512_add_epi8(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return _mm512_sub_epi8(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } - // Addition/subtraction are the same for signed and unsigned - simdjson_inline simd8 operator+(const simd8 other) const { return _mm512_add_epi8(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return _mm512_sub_epi8(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - // Override to distinguish from bool version - simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 lookup_table) const { + return _mm512_shuffle_epi8(lookup_table, *this); + } - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 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 compress(uint32_t mask) would be + // sensible, but the AVX ISA makes this kind of approach difficult. + template + 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 + } - // 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 compress(uint32_t mask) would be - // sensible, but the AVX ISA makes this kind of approach difficult. - template - simdjson_inline void compress(uint64_t mask, L* output) const { - _mm512_mask_compressstoreu_epi8(output, ~mask, *this); - } + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; - template - simdjson_inline simd8 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::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - }; + // Signed bytes + template<> + struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m512i _value) : base8_numeric(_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 + )) {} - // Signed bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m512i _value) : base8_numeric(_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 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( + 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 + ); + } - // Repeat 16 values as many times as necessary (usually for lookup tables) - simdjson_inline static simd8 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( - 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 max_val(const simd8 other) const { return _mm512_max_epi8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm512_min_epi8(*this, other); } - // Order-sensitive comparisons - simdjson_inline simd8 max_val(const simd8 other) const { return _mm512_max_epi8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm512_min_epi8(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return _mm512_maskz_abs_epi8(_mm512_cmpgt_epi8_mask(*this, other),_mm512_set1_epi8(uint8_t(0x80))); } + simdjson_inline simd8 operator<(const simd8 other) const { return _mm512_maskz_abs_epi8(_mm512_cmpgt_epi8_mask(other, *this),_mm512_set1_epi8(uint8_t(0x80))); } + }; - simdjson_inline simd8 operator>(const simd8 other) const { return _mm512_maskz_abs_epi8(_mm512_cmpgt_epi8_mask(*this, other), _mm512_set1_epi8(uint8_t(0x80))); } - simdjson_inline simd8 operator<(const simd8 other) const { return _mm512_maskz_abs_epi8(_mm512_cmpgt_epi8_mask(other, *this), _mm512_set1_epi8(uint8_t(0x80))); } - }; + // Unsigned bytes + template<> + struct simd8: base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m512i _value) : base8_numeric(_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 + )) {} - // Unsigned bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m512i _value) : base8_numeric(_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 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( + 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 + ); + } - // Repeat 16 values as many times as necessary (usually for lookup tables) - simdjson_inline static simd8 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( - 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 saturating_add(const simd8 other) const { return _mm512_adds_epu8(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm512_subs_epu8(*this, other); } - // Saturated math - simdjson_inline simd8 saturating_add(const simd8 other) const { return _mm512_adds_epu8(*this, other); } - simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm512_subs_epu8(*this, other); } + // Order-specific operations + simdjson_inline simd8 max_val(const simd8 other) const { return _mm512_max_epu8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm512_min_epu8(other, *this); } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } + simdjson_inline uint64_t operator<=(const simd8 other) const { return other.max_val(*this) == other; } + simdjson_inline uint64_t operator>=(const simd8 other) const { return other.min_val(*this) == other; } + simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } - // Order-specific operations - simdjson_inline simd8 max_val(const simd8 other) const { return _mm512_max_epu8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm512_min_epu8(other, *this); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } - simdjson_inline uint64_t operator<=(const simd8 other) const { return other.max_val(*this) == other; } - simdjson_inline uint64_t operator>=(const simd8 other) const { return other.min_val(*this) == other; } - simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { return _mm512_mask_blend_epi8(*this == uint8_t(0), _mm512_set1_epi8(0), _mm512_set1_epi8(-1)); } + simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } + simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } + simdjson_inline simd8 any_bits_set(simd8 bits) const { return ~this->bits_not_set(bits); } - // Bit-specific operations - simdjson_inline simd8 bits_not_set() const { return _mm512_mask_blend_epi8(*this == uint8_t(0), _mm512_set1_epi8(0), _mm512_set1_epi8(-1)); } - simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } - simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } - simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return !_mm512_test_epi8_mask(*this, bits); } + simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } + template + simdjson_inline simd8 shr() const { return simd8(_mm512_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } + template + simdjson_inline simd8 shl() const { return simd8(_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 + simdjson_inline uint64_t get_bit() const { return _mm512_movepi8_mask(_mm512_slli_epi16(*this, 7-N)); } + }; - 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 bits) const { return !_mm512_test_epi8_mask(*this, bits); } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } - template - simdjson_inline simd8 shr() const { return simd8(_mm512_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } - template - simdjson_inline simd8 shl() const { return simd8(_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 - simdjson_inline uint64_t get_bit() const { return _mm512_movepi8_mask(_mm512_slli_epi16(*this, 7 - N)); } - }; + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 1, "Icelake kernel should use one register per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 1, "Icelake kernel should use one register per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1) : chunks{chunk0, chunk1} {} + simdjson_inline simd8x64(const simd8 chunk0) : chunks{chunk0} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr)} {} - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1) : chunks{ chunk0, chunk1 } {} - simdjson_inline simd8x64(const simd8 chunk0) : chunks{ chunk0 } {} - simdjson_inline simd8x64(const T ptr[64]) : chunks{ simd8::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 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)*0); + } - simdjson_inline void store(T ptr[64]) const { - this->chunks[0].store(ptr + sizeof(simd8) * 0); - } + simdjson_inline simd8 reduce_or() const { + return this->chunks[0]; + } - simdjson_inline simd8 reduce_or() const { - return this->chunks[0]; - } + simdjson_inline simd8x64 bit_or(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] | mask + ); + } - simdjson_inline simd8x64 bit_or(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] | mask - ); - } + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return this->chunks[0] == mask; + } - simdjson_inline uint64_t eq(const T m) const { - const simd8 mask = simd8::splat(m); - return this->chunks[0] == mask; - } + simdjson_inline uint64_t eq(const simd8x64 &other) const { + return this->chunks[0] == other.chunks[0]; + } - simdjson_inline uint64_t eq(const simd8x64& other) const { - return this->chunks[0] == other.chunks[0]; - } + simdjson_inline uint64_t lteq(const T m) const { + const simd8 mask = simd8::splat(m); + return this->chunks[0] <= mask; + } + }; // struct simd8x64 - simdjson_inline uint64_t lteq(const T m) const { - const simd8 mask = simd8::splat(m); - return this->chunks[0] <= mask; - } - }; // struct simd8x64 +} // namespace simd - } // namespace simd - - } // unnamed namespace - } // namespace icelake +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_ICELAKE_SIMD_H @@ -20995,41 +22443,70 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { - namespace { +namespace icelake { +namespace { - using namespace simd; +using namespace simd; - // Holds backslashes and quotes locations. - struct backslash_and_quote { - public: - static constexpr uint32_t BYTES_PROCESSED = 64; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t* src, uint8_t* dst); +// 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); } + 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 + 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 v(src); - // store to dest unconditionally - we can overwrite the bits we don't like later - v.store(dst); - return { - static_cast(v == '\\'), // bs_bits - static_cast(v == '"'), // quote_bits - }; - } +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 v(src); + // store to dest unconditionally - we can overwrite the bits we don't like later + v.store(dst); + return { + static_cast(v == '\\'), // bs_bits + static_cast(v == '"'), // quote_bits + }; +} - } // unnamed namespace - } // namespace icelake + + +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 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 @@ -21046,48 +22523,48 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { - namespace numberparsing { +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(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 - } +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(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; +/** @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; + // 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 + 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); + __uint128_t r = (static_cast<__uint128_t>(value1)) * value2; + answer.low = uint64_t(r); + answer.high = uint64_t(r >> 64); #endif - return answer; - } + return answer; +} - } // namespace numberparsing - } // namespace icelake +} // namespace numberparsing +} // namespace icelake } // namespace simdjson #define SIMDJSON_SWAR_NUMBER_PARSING 1 @@ -21122,10 +22599,12 @@ namespace simdjson { /* 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_LSX */ -/* amalgamation skipped (editor-only): #include "simdjson/lsx/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 */ @@ -21135,22 +22614,22 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { +namespace icelake { - struct open_container; - class dom_parser_implementation; +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 - }; +/** + * 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 icelake } // namespace simdjson #endif // SIMDJSON_GENERIC_BASE_H @@ -21167,99 +22646,97 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { - namespace { - namespace jsoncharutils { +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]; - } +// 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]; - } +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; - } +// 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 - } +// 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; - } +// 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 jsoncharutils +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_GENERIC_JSONCHARUTILS_H @@ -21277,69 +22754,69 @@ namespace simdjson { #include namespace simdjson { - namespace icelake { - namespace { - /// @private - namespace atomparsing { +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; } +// 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; } - // 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); - } +// 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); +} - 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) { + 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_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) { + 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_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) { + 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; } - } +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; } +} - } // namespace atomparsing - } // unnamed namespace - } // namespace icelake +} // namespace atomparsing +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_GENERIC_ATOMPARSING_H @@ -21355,83 +22832,83 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { +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 +// 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"); +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_containers{}; - /** Whether each open container is a [ or { */ - std::unique_ptr 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{}; +class dom_parser_implementation final : public internal::dom_parser_implementation { +public: + /** Tape location of each open { or [ */ + std::unique_ptr open_containers{}; + /** Whether each open container is a [ or { */ + std::unique_ptr 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; + 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); + 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 icelake } // namespace simdjson namespace simdjson { - namespace icelake { +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; +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) { return CAPACITY; } - // Stage 1 index output - size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; - 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; +// 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) { return CAPACITY; } + // Stage 1 index output + size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; + 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; - } + _capacity = capacity; + return SUCCESS; +} - inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { - // 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; } +inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { + // 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; - } + _max_depth = max_depth; + return SUCCESS; +} - } // namespace icelake +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H @@ -21446,129 +22923,148 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { +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 : public internal::implementation_simdjson_result_base { - * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} - * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} - * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} - * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} - * // Your extra methods here - * } - * - * Then any method returning simdjson_result will be chainable with your methods. - */ - template - struct implementation_simdjson_result_base { +// 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 : public internal::implementation_simdjson_result_base { + * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} + * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} + * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} + * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} + * // Your extra methods here + * } + * + * Then any method returning simdjson_result will be chainable with your methods. + */ +template +struct implementation_simdjson_result_base { - /** - * Create a new empty result with error = UNINITIALIZED. - */ - simdjson_inline implementation_simdjson_result_base() noexcept = default; + /** + * 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 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 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; + /** + * 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 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; + /** + * 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_inline error_code error() const 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& value() & noexcept(false); + /** + * 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); - /** - * Take the result value (move it). - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline T&& value() && 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&& take_value() && noexcept(false); + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& 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); + /** + * 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; - 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 + /** + * 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; - } // namespace icelake + 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 @@ -21589,8 +23085,8 @@ namespace simdjson { #include namespace simdjson { - namespace icelake { - namespace numberparsing { +namespace icelake { +namespace numberparsing { #ifdef JSON_TEST_NUMBERS #define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) @@ -21606,540 +23102,547 @@ namespace simdjson { #define BIGINT_NUMBER(SRC) (BIGINT_ERROR) #endif - namespace { +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(negative)) << 63); - std::memcpy(&d, &mantissa, sizeof(d)); - return d; - } +// 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(negative)) << 63); + std::memcpy(&d, &mantissa, sizeof(d)); + return d; +} - // 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 +// 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) + if (0 <= power && power <= 22 && i <= 9007199254740991) #else - if (-22 <= power && power <= 22 && i <= 9007199254740991) + 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 + { + // 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. + // 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; - } + // 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 - // approximatively 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; + // 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 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. - simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]); - // 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. + // 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 - // 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. - simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); - 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); + // 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. - // 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 - } - } + // 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); - mantissa += mantissa & 1; - mantissa >>= 1; + // 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 + } + } - // 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; - } + mantissa += mantissa & 1; + mantissa >>= 1; - // 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(ptr)); - // We do not accept infinite values. + // 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; +} - // 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::max)() || *outDouble < std::numeric_limits::lowest()); - } +// 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(ptr)); + // We do not accept infinite values. - static bool parse_float_fallback(const uint8_t* ptr, const uint8_t* end_ptr, double* outDouble) { - *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(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::max)() || *outDouble < std::numeric_limits::lowest()); +} - // 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::max)() || *outDouble < std::numeric_limits::lowest()); - } +static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) { + *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(end_ptr)); + // We do not accept infinite values. - // 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); - } + // 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::max)() || *outDouble < std::numeric_limits::lowest()); +} - template - 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(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; - } +// 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); +} - simdjson_inline bool is_digit(const uint8_t c) { - return static_cast(c - '0') <= 9; - } +template +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(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 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; +simdjson_inline bool is_digit(const uint8_t c) { + return static_cast(c - '0') <= 9; +} + +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; #ifdef SIMDJSON_SWAR_NUMBER_PARSING #if SIMDJSON_SWAR_NUMBER_PARSING - // this helps if we have lots of decimals! - // this turns out to be frequent enough. - if (is_made_of_eight_digits_fast(p)) { - i = i * 100000000 + parse_eight_digits_unrolled(p); - p += 8; - } + // this helps if we have lots of decimals! + // this turns out to be frequent enough. + if (is_made_of_eight_digits_fast(p)) { + i = i * 100000000 + parse_eight_digits_unrolled(p); + p += 8; + } #endif // SIMDJSON_SWAR_NUMBER_PARSING #endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING - // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) - if (parse_digit(*p, i)) { ++p; } - while (parse_digit(*p, i)) { p++; } - exponent = first_after_period - p; - // Decimal without digits (123.) is illegal - if (exponent == 0) { - return INVALID_NUMBER(src); - } - return SUCCESS; - } + // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) + if (parse_digit(*p, i)) { ++p; } + while (parse_digit(*p, i)) { p++; } + exponent = first_after_period - p; + // Decimal without digits (123.) is illegal + if (exponent == 0) { + return INVALID_NUMBER(src); + } + return SUCCESS; +} - 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 +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. + // 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. + // 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 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; - } + // 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 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); - } +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 +} // unnamed namespace - /** @private */ - static 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 */ +static 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 - 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; - } +/** @private */ +template +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 - simdjson_inline error_code parse_number(const uint8_t* const src, W& writer); +// 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 +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 +// for performance analysis, it is sometimes useful to skip parsing #ifdef SIMDJSON_SKIPNUMBERPARSING - template - simdjson_inline error_code parse_number(const uint8_t* const, W& writer) { - writer.append_s64(0); // always write zero - return SUCCESS; // always succeeds - } +template +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 parse_unsigned(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline bool is_negative(const uint8_t* src) noexcept { return false; } - simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t* src) noexcept { return false; } - simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t* src) noexcept { return number_type::signed_integer; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t * src) noexcept { return number_type::signed_integer; } #else // parse the number at src @@ -22151,744 +23654,753 @@ namespace simdjson { // content and append a space before calling this function. // // Our objective is accurate parsing (ULP of 0) at high speed. - template - 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)) { 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 INVALID_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 { - WRITE_INTEGER(negative ? (~i + 1) : i, src, writer); - } - 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 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; - 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); - // 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 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 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; - 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); - // 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 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; - 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); - // 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 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 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; - while (parse_digit(*src, i)) { src++; } - - // 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 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; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (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 == '.')) { - p++; - const uint8_t* start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (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 = 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; - } - - simdjson_unused simdjson_inline bool is_negative(const uint8_t* src) noexcept { - return (*src == '-'); - } - - simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t* src) noexcept { - bool negative = (*src == '-'); - src += uint8_t(negative); - const uint8_t* p = src; - while (static_cast(*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 get_number_type(const uint8_t* src) noexcept { - bool negative = (*src == '-'); - src += uint8_t(negative); - const uint8_t* p = src; - while (static_cast(*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("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; - } - 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("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 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 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; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (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 == '.')) { - p++; - const uint8_t* start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (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 = 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; - } - - } // unnamed namespace +template +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)) { 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 INVALID_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 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; + 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); + // 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 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 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; + 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); + // 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 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; + 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); + // 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 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 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; + while (parse_digit(*src, i)) { src++; } + + // 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { + return (*src == '-'); +} + +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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 get_number_type(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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("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("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 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +} // unnamed namespace #endif // SIMDJSON_SKIPNUMBERPARSING - } // namespace numberparsing +} // 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; - } +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 icelake } // namespace simdjson #endif // SIMDJSON_GENERIC_NUMBERPARSING_H @@ -22905,84 +24417,113 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { +namespace icelake { - // - // internal::implementation_simdjson_result_base inline implementation - // +// +// internal::implementation_simdjson_result_base inline implementation +// - template - simdjson_inline void implementation_simdjson_result_base::tie(T& value, error_code& error) && noexcept { - error = this->second; - if (!error) { - value = std::forward>(*this).first; - } - } +template +simdjson_inline void implementation_simdjson_result_base::tie(T &value, error_code &error) && noexcept { + error = this->second; + if (!error) { + value = std::forward>(*this).first; + } +} - template - simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T& value) && noexcept { - error_code error; - std::forward>(*this).tie(value, error); - return error; - } +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T &value) && noexcept { + error_code error; + std::forward>(*this).tie(value, error); + return error; +} - template - simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { - return this->second; - } +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { + return this->second; +} + + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} #if SIMDJSON_EXCEPTIONS - template - simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return this->first; - } +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} - template - simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { - return std::forward>(*this).take_value(); - } +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} - template - simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return std::forward(this->first); - } +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} - template - simdjson_inline implementation_simdjson_result_base::operator T && () && noexcept(false) { - return std::forward>(*this).take_value(); - } + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + +template +simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return this->first; +} + +template +simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { + return std::forward>(*this).take_value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return std::forward(this->first); +} + +template +simdjson_inline implementation_simdjson_result_base::operator T&&() && noexcept(false) { + return std::forward>(*this).take_value(); +} #endif // SIMDJSON_EXCEPTIONS - template - simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { - return this->first; - } +template +simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { + return this->first; +} - template - simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { - return this->first; - } +template +simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { + return this->first; +} - template - simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { - return std::forward(this->first); - } +template +simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { + return std::forward(this->first); +} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T&& value, error_code error) noexcept - : first{ std::forward(value) }, second{ error } {} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept - : implementation_simdjson_result_base(T{}, error) {} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T&& value) noexcept - : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value, error_code error) noexcept + : first{std::forward(value)}, second{error} {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept + : implementation_simdjson_result_base(T{}, error) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value) noexcept + : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} - } // namespace icelake +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H @@ -23017,28 +24558,28 @@ SIMDJSON_UNTARGET_REGION // The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_ICELAKE namespace simdjson { - namespace icelake { +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& 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; - }; +/** + * @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& 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 icelake } // namespace simdjson #endif // SIMDJSON_ICELAKE_IMPLEMENTATION_H @@ -23062,14 +24603,14 @@ namespace simdjson { // 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 { +/** + * Implementation for Icelake (Intel AVX512). + */ +namespace icelake { - class implementation; +class implementation; - } // namespace icelake +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_ICELAKE_BASE_H @@ -23117,7 +24658,7 @@ namespace simdjson { #include #include #include // for _mm_clmulepi64_si128 - // Important: we need the AVX-512 headers: +// Important: we need the AVX-512 headers: #include #include #include @@ -23153,64 +24694,64 @@ SIMDJSON_TARGET_REGION("avx512f,avx512dq,avx512cd,avx512bw,avx512vbmi,avx512vbmi /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { - namespace { +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) { +// 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); + 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); + //////// + // 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 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)); - } +/* 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 - } +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); - } +simdjson_inline long long int count_ones(uint64_t input_num) { + return _popcnt64(input_num); +} #endif - simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, - uint64_t* result) { +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { #if SIMDJSON_REGULAR_VISUAL_STUDIO - return _addcarry_u64(0, value1, value2, - reinterpret_cast(result)); + return _addcarry_u64(0, value1, value2, + reinterpret_cast(result)); #else - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); #endif - } +} - } // unnamed namespace - } // namespace icelake +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_ICELAKE_BITMANIPULATION_H @@ -23226,24 +24767,24 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { - namespace { +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); - } +// +// 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 +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_ICELAKE_BITMASK_H @@ -23271,354 +24812,358 @@ namespace simdjson { * 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)); + 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 { +namespace icelake { +namespace { +namespace simd { - // Forward-declared so they can be used by splat and friends. - template - struct base { - __m512i value; + // Forward-declared so they can be used by splat and friends. + template + struct base { + __m512i value; - // Zero constructor - simdjson_inline base() : value{ __m512i() } {} + // Zero constructor + simdjson_inline base() : value{__m512i()} {} - // Conversion from SIMD register - simdjson_inline base(const __m512i _value) : value(_value) {} + // 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; } + // 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(this); *this_cast = *this_cast | other; return *this_cast; } - simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } - simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } - }; + // 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(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; - // Forward-declared so they can be used by splat and friends. - template - struct simd8; + // Forward-declared so they can be used by splat and friends. + template + struct simd8; - template> - struct base8 : base> { - typedef uint32_t bitmask_t; - typedef uint64_t bitmask2_t; + template> + struct base8: base> { + typedef uint32_t bitmask_t; + typedef uint64_t bitmask2_t; - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m512i _value) : base>(_value) {} + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m512i _value) : base>(_value) {} - friend simdjson_really_inline uint64_t operator==(const simd8 lhs, const simd8 rhs) { - return _mm512_cmpeq_epi8_mask(lhs, rhs); - } + friend simdjson_really_inline uint64_t operator==(const simd8 lhs, const simd8 rhs) { + return _mm512_cmpeq_epi8_mask(lhs, rhs); + } + static const int SIZE = sizeof(base::value); - static const int SIZE = sizeof(base::value); + template + simdjson_inline simd8 prev(const simd8 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); + } + }; - template - simdjson_inline simd8 prev(const simd8 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: base8 { + static simdjson_inline simd8 splat(bool _value) { return _mm512_set1_epi8(uint8_t(-(!!_value))); } - // SIMD byte mask type (returned by things like eq and gt) - template<> - struct simd8 : base8 { - static simdjson_inline simd8 splat(bool _value) { return _mm512_set1_epi8(uint8_t(-(!!_value))); } + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m512i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(splat(_value)) {} + simdjson_inline bool any() const { return !!_mm512_test_epi8_mask (*this, *this); } + simdjson_inline simd8 operator~() const { return *this ^ true; } + }; - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m512i _value) : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) : base8(splat(_value)) {} - simdjson_inline bool any() const { return !!_mm512_test_epi8_mask(*this, *this); } - simdjson_inline simd8 operator~() const { return *this ^ true; } - }; + template + struct base8_numeric: base8 { + static simdjson_inline simd8 splat(T _value) { return _mm512_set1_epi8(_value); } + static simdjson_inline simd8 zero() { return _mm512_setzero_si512(); } + static simdjson_inline simd8 load(const T values[64]) { + return _mm512_loadu_si512(reinterpret_cast(values)); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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( + 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 + ); + } - template - struct base8_numeric : base8 { - static simdjson_inline simd8 splat(T _value) { return _mm512_set1_epi8(_value); } - static simdjson_inline simd8 zero() { return _mm512_setzero_si512(); } - static simdjson_inline simd8 load(const T values[64]) { - return _mm512_loadu_si512(reinterpret_cast(values)); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdjson_inline simd8 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( - 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() {} + simdjson_inline base8_numeric(const __m512i _value) : base8(_value) {} - simdjson_inline base8_numeric() : base8() {} - simdjson_inline base8_numeric(const __m512i _value) : base8(_value) {} + // Store to array + simdjson_inline void store(T dst[64]) const { return _mm512_storeu_si512(reinterpret_cast<__m512i *>(dst), *this); } - // 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 operator+(const simd8 other) const { return _mm512_add_epi8(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return _mm512_sub_epi8(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } - // Addition/subtraction are the same for signed and unsigned - simdjson_inline simd8 operator+(const simd8 other) const { return _mm512_add_epi8(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return _mm512_sub_epi8(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - // Override to distinguish from bool version - simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 lookup_table) const { + return _mm512_shuffle_epi8(lookup_table, *this); + } - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 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 compress(uint32_t mask) would be + // sensible, but the AVX ISA makes this kind of approach difficult. + template + 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 + } - // 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 compress(uint32_t mask) would be - // sensible, but the AVX ISA makes this kind of approach difficult. - template - simdjson_inline void compress(uint64_t mask, L* output) const { - _mm512_mask_compressstoreu_epi8(output, ~mask, *this); - } + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; - template - simdjson_inline simd8 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::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - }; + // Signed bytes + template<> + struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m512i _value) : base8_numeric(_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 + )) {} - // Signed bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m512i _value) : base8_numeric(_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 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( + 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 + ); + } - // Repeat 16 values as many times as necessary (usually for lookup tables) - simdjson_inline static simd8 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( - 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 max_val(const simd8 other) const { return _mm512_max_epi8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm512_min_epi8(*this, other); } - // Order-sensitive comparisons - simdjson_inline simd8 max_val(const simd8 other) const { return _mm512_max_epi8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm512_min_epi8(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return _mm512_maskz_abs_epi8(_mm512_cmpgt_epi8_mask(*this, other),_mm512_set1_epi8(uint8_t(0x80))); } + simdjson_inline simd8 operator<(const simd8 other) const { return _mm512_maskz_abs_epi8(_mm512_cmpgt_epi8_mask(other, *this),_mm512_set1_epi8(uint8_t(0x80))); } + }; - simdjson_inline simd8 operator>(const simd8 other) const { return _mm512_maskz_abs_epi8(_mm512_cmpgt_epi8_mask(*this, other), _mm512_set1_epi8(uint8_t(0x80))); } - simdjson_inline simd8 operator<(const simd8 other) const { return _mm512_maskz_abs_epi8(_mm512_cmpgt_epi8_mask(other, *this), _mm512_set1_epi8(uint8_t(0x80))); } - }; + // Unsigned bytes + template<> + struct simd8: base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m512i _value) : base8_numeric(_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 + )) {} - // Unsigned bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m512i _value) : base8_numeric(_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 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( + 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 + ); + } - // Repeat 16 values as many times as necessary (usually for lookup tables) - simdjson_inline static simd8 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( - 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 saturating_add(const simd8 other) const { return _mm512_adds_epu8(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm512_subs_epu8(*this, other); } - // Saturated math - simdjson_inline simd8 saturating_add(const simd8 other) const { return _mm512_adds_epu8(*this, other); } - simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm512_subs_epu8(*this, other); } + // Order-specific operations + simdjson_inline simd8 max_val(const simd8 other) const { return _mm512_max_epu8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm512_min_epu8(other, *this); } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } + simdjson_inline uint64_t operator<=(const simd8 other) const { return other.max_val(*this) == other; } + simdjson_inline uint64_t operator>=(const simd8 other) const { return other.min_val(*this) == other; } + simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } - // Order-specific operations - simdjson_inline simd8 max_val(const simd8 other) const { return _mm512_max_epu8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm512_min_epu8(other, *this); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } - simdjson_inline uint64_t operator<=(const simd8 other) const { return other.max_val(*this) == other; } - simdjson_inline uint64_t operator>=(const simd8 other) const { return other.min_val(*this) == other; } - simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { return _mm512_mask_blend_epi8(*this == uint8_t(0), _mm512_set1_epi8(0), _mm512_set1_epi8(-1)); } + simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } + simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } + simdjson_inline simd8 any_bits_set(simd8 bits) const { return ~this->bits_not_set(bits); } - // Bit-specific operations - simdjson_inline simd8 bits_not_set() const { return _mm512_mask_blend_epi8(*this == uint8_t(0), _mm512_set1_epi8(0), _mm512_set1_epi8(-1)); } - simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } - simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } - simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return !_mm512_test_epi8_mask(*this, bits); } + simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } + template + simdjson_inline simd8 shr() const { return simd8(_mm512_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } + template + simdjson_inline simd8 shl() const { return simd8(_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 + simdjson_inline uint64_t get_bit() const { return _mm512_movepi8_mask(_mm512_slli_epi16(*this, 7-N)); } + }; - 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 bits) const { return !_mm512_test_epi8_mask(*this, bits); } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } - template - simdjson_inline simd8 shr() const { return simd8(_mm512_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } - template - simdjson_inline simd8 shl() const { return simd8(_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 - simdjson_inline uint64_t get_bit() const { return _mm512_movepi8_mask(_mm512_slli_epi16(*this, 7 - N)); } - }; + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 1, "Icelake kernel should use one register per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 1, "Icelake kernel should use one register per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1) : chunks{chunk0, chunk1} {} + simdjson_inline simd8x64(const simd8 chunk0) : chunks{chunk0} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr)} {} - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1) : chunks{ chunk0, chunk1 } {} - simdjson_inline simd8x64(const simd8 chunk0) : chunks{ chunk0 } {} - simdjson_inline simd8x64(const T ptr[64]) : chunks{ simd8::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 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)*0); + } - simdjson_inline void store(T ptr[64]) const { - this->chunks[0].store(ptr + sizeof(simd8) * 0); - } + simdjson_inline simd8 reduce_or() const { + return this->chunks[0]; + } - simdjson_inline simd8 reduce_or() const { - return this->chunks[0]; - } + simdjson_inline simd8x64 bit_or(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] | mask + ); + } - simdjson_inline simd8x64 bit_or(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] | mask - ); - } + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return this->chunks[0] == mask; + } - simdjson_inline uint64_t eq(const T m) const { - const simd8 mask = simd8::splat(m); - return this->chunks[0] == mask; - } + simdjson_inline uint64_t eq(const simd8x64 &other) const { + return this->chunks[0] == other.chunks[0]; + } - simdjson_inline uint64_t eq(const simd8x64& other) const { - return this->chunks[0] == other.chunks[0]; - } + simdjson_inline uint64_t lteq(const T m) const { + const simd8 mask = simd8::splat(m); + return this->chunks[0] <= mask; + } + }; // struct simd8x64 - simdjson_inline uint64_t lteq(const T m) const { - const simd8 mask = simd8::splat(m); - return this->chunks[0] <= mask; - } - }; // struct simd8x64 +} // namespace simd - } // namespace simd - - } // unnamed namespace - } // namespace icelake +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_ICELAKE_SIMD_H @@ -23635,41 +25180,70 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { - namespace { +namespace icelake { +namespace { - using namespace simd; +using namespace simd; - // Holds backslashes and quotes locations. - struct backslash_and_quote { - public: - static constexpr uint32_t BYTES_PROCESSED = 64; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t* src, uint8_t* dst); +// 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); } + 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 + 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 v(src); - // store to dest unconditionally - we can overwrite the bits we don't like later - v.store(dst); - return { - static_cast(v == '\\'), // bs_bits - static_cast(v == '"'), // quote_bits - }; - } +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 v(src); + // store to dest unconditionally - we can overwrite the bits we don't like later + v.store(dst); + return { + static_cast(v == '\\'), // bs_bits + static_cast(v == '"'), // quote_bits + }; +} - } // unnamed namespace - } // namespace icelake + + +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 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 @@ -23686,48 +25260,48 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { - namespace numberparsing { +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(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 - } +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(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; +/** @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; + // 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 + 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); + __uint128_t r = (static_cast<__uint128_t>(value1)) * value2; + answer.low = uint64_t(r); + answer.high = uint64_t(r >> 64); #endif - return answer; - } + return answer; +} - } // namespace numberparsing - } // namespace icelake +} // namespace numberparsing +} // namespace icelake } // namespace simdjson #define SIMDJSON_SWAR_NUMBER_PARSING 1 @@ -23752,13 +25326,13 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { - namespace { +namespace icelake { +namespace { - struct json_character_block; +struct json_character_block; - } // unnamed namespace - } // namespace icelake +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_BASE_H @@ -23775,14 +25349,14 @@ namespace simdjson { // Interface a dom parser implementation must fulfill namespace simdjson { - namespace icelake { - namespace { +namespace icelake { +namespace { - simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3); - simdjson_inline bool is_ascii(const simd8x64& input); +simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3); +simdjson_inline bool is_ascii(const simd8x64& input); - } // unnamed namespace - } // namespace icelake +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_DOM_PARSER_IMPLEMENTATION_H @@ -23797,22 +25371,22 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { - namespace { +namespace icelake { +namespace { - struct json_character_block { - static simdjson_inline json_character_block classify(const simd::simd8x64& in); +struct json_character_block { + static simdjson_inline json_character_block classify(const simd::simd8x64& in); - simdjson_inline uint64_t whitespace() const noexcept { return _whitespace; } - simdjson_inline uint64_t op() const noexcept { return _op; } - simdjson_inline uint64_t scalar() const noexcept { return ~(op() | whitespace()); } + simdjson_inline uint64_t whitespace() const noexcept { return _whitespace; } + simdjson_inline uint64_t op() const noexcept { return _op; } + simdjson_inline uint64_t scalar() const noexcept { return ~(op() | whitespace()); } - uint64_t _whitespace; - uint64_t _op; - }; + uint64_t _whitespace; + uint64_t _op; +}; - } // unnamed namespace - } // namespace icelake +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_JSON_CHARACTER_BLOCK_H @@ -23831,30 +25405,30 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { - namespace { - namespace stage1 { +namespace icelake { +namespace { +namespace stage1 { - class bit_indexer; - template - struct buf_block_reader; - struct json_block; - class json_minifier; - class json_scanner; - struct json_string_block; - class json_string_scanner; - class json_structural_indexer; +class bit_indexer; +template +struct buf_block_reader; +struct json_block; +class json_minifier; +class json_scanner; +struct json_string_block; +class json_string_scanner; +class json_structural_indexer; - } // namespace stage1 +} // namespace stage1 - namespace utf8_validation { - struct utf8_checker; - } // namespace utf8_validation +namespace utf8_validation { +struct utf8_checker; +} // namespace utf8_validation - using utf8_validation::utf8_checker; +using utf8_validation::utf8_checker; - } // unnamed namespace - } // namespace icelake +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_BASE_H @@ -23871,109 +25445,109 @@ namespace simdjson { #include namespace simdjson { - namespace icelake { - namespace { - namespace stage1 { +namespace icelake { +namespace { +namespace stage1 { - // Walks through a buffer in block-sized increments, loading the last part with spaces - template - struct buf_block_reader { - public: - simdjson_inline buf_block_reader(const uint8_t* _buf, size_t _len); - simdjson_inline size_t block_index(); - simdjson_inline bool has_full_block() const; - simdjson_inline const uint8_t* full_block() const; - /** - * Get the last block, padded with spaces. - * - * There will always be a last block, with at least 1 byte, unless len == 0 (in which case this - * function fills the buffer with spaces and returns 0. In particular, if len == STEP_SIZE there - * will be 0 full_blocks and 1 remainder block with STEP_SIZE bytes and no spaces for padding. - * - * @return the number of effective characters in the last block. - */ - simdjson_inline size_t get_remainder(uint8_t* dst) const; - simdjson_inline void advance(); - private: - const uint8_t* buf; - const size_t len; - const size_t lenminusstep; - size_t idx; - }; +// Walks through a buffer in block-sized increments, loading the last part with spaces +template +struct buf_block_reader { +public: + simdjson_inline buf_block_reader(const uint8_t *_buf, size_t _len); + simdjson_inline size_t block_index(); + simdjson_inline bool has_full_block() const; + simdjson_inline const uint8_t *full_block() const; + /** + * Get the last block, padded with spaces. + * + * There will always be a last block, with at least 1 byte, unless len == 0 (in which case this + * function fills the buffer with spaces and returns 0. In particular, if len == STEP_SIZE there + * will be 0 full_blocks and 1 remainder block with STEP_SIZE bytes and no spaces for padding. + * + * @return the number of effective characters in the last block. + */ + simdjson_inline size_t get_remainder(uint8_t *dst) const; + simdjson_inline void advance(); +private: + const uint8_t *buf; + const size_t len; + const size_t lenminusstep; + size_t idx; +}; - // Routines to print masks and text for debugging bitmask operations - simdjson_unused static char* format_input_text_64(const uint8_t* text) { - static char buf[sizeof(simd8x64) + 1]; - for (size_t i = 0; i < sizeof(simd8x64); i++) { - buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]); - } - buf[sizeof(simd8x64)] = '\0'; - return buf; - } +// Routines to print masks and text for debugging bitmask operations +simdjson_unused static char * format_input_text_64(const uint8_t *text) { + static char buf[sizeof(simd8x64) + 1]; + for (size_t i=0; i); i++) { + buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]); + } + buf[sizeof(simd8x64)] = '\0'; + return buf; +} - // Routines to print masks and text for debugging bitmask operations - simdjson_unused static char* format_input_text(const simd8x64& in) { - static char buf[sizeof(simd8x64) + 1]; - in.store(reinterpret_cast(buf)); - for (size_t i = 0; i < sizeof(simd8x64); i++) { - if (buf[i] < ' ') { buf[i] = '_'; } - } - buf[sizeof(simd8x64)] = '\0'; - return buf; - } +// Routines to print masks and text for debugging bitmask operations +simdjson_unused static char * format_input_text(const simd8x64& in) { + static char buf[sizeof(simd8x64) + 1]; + in.store(reinterpret_cast(buf)); + for (size_t i=0; i); i++) { + if (buf[i] < ' ') { buf[i] = '_'; } + } + buf[sizeof(simd8x64)] = '\0'; + return buf; +} - simdjson_unused static char* format_input_text(const simd8x64& in, uint64_t mask) { - static char buf[sizeof(simd8x64) + 1]; - in.store(reinterpret_cast(buf)); - for (size_t i = 0; i < sizeof(simd8x64); i++) { - if (buf[i] <= ' ') { buf[i] = '_'; } - if (!(mask & (size_t(1) << i))) { buf[i] = ' '; } - } - buf[sizeof(simd8x64)] = '\0'; - return buf; - } +simdjson_unused static char * format_input_text(const simd8x64& in, uint64_t mask) { + static char buf[sizeof(simd8x64) + 1]; + in.store(reinterpret_cast(buf)); + for (size_t i=0; i); i++) { + if (buf[i] <= ' ') { buf[i] = '_'; } + if (!(mask & (size_t(1) << i))) { buf[i] = ' '; } + } + buf[sizeof(simd8x64)] = '\0'; + return buf; +} - simdjson_unused static char* format_mask(uint64_t mask) { - static char buf[sizeof(simd8x64) + 1]; - for (size_t i = 0; i < 64; i++) { - buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' '; - } - buf[64] = '\0'; - return buf; - } +simdjson_unused static char * format_mask(uint64_t mask) { + static char buf[sizeof(simd8x64) + 1]; + for (size_t i=0; i<64; i++) { + buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' '; + } + buf[64] = '\0'; + return buf; +} - template - simdjson_inline buf_block_reader::buf_block_reader(const uint8_t* _buf, size_t _len) : buf{ _buf }, len{ _len }, lenminusstep{ len < STEP_SIZE ? 0 : len - STEP_SIZE }, idx{ 0 } {} +template +simdjson_inline buf_block_reader::buf_block_reader(const uint8_t *_buf, size_t _len) : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE}, idx{0} {} - template - simdjson_inline size_t buf_block_reader::block_index() { return idx; } +template +simdjson_inline size_t buf_block_reader::block_index() { return idx; } - template - simdjson_inline bool buf_block_reader::has_full_block() const { - return idx < lenminusstep; - } +template +simdjson_inline bool buf_block_reader::has_full_block() const { + return idx < lenminusstep; +} - template - simdjson_inline const uint8_t* buf_block_reader::full_block() const { - return &buf[idx]; - } +template +simdjson_inline const uint8_t *buf_block_reader::full_block() const { + return &buf[idx]; +} - template - simdjson_inline size_t buf_block_reader::get_remainder(uint8_t* dst) const { - if (len == idx) { return 0; } // memcpy(dst, null, 0) will trigger an error with some sanitizers - std::memset(dst, 0x20, STEP_SIZE); // std::memset STEP_SIZE because it's more efficient to write out 8 or 16 bytes at once. - std::memcpy(dst, buf + idx, len - idx); - return len - idx; - } +template +simdjson_inline size_t buf_block_reader::get_remainder(uint8_t *dst) const { + if(len == idx) { return 0; } // memcpy(dst, null, 0) will trigger an error with some sanitizers + std::memset(dst, 0x20, STEP_SIZE); // std::memset STEP_SIZE because it's more efficient to write out 8 or 16 bytes at once. + std::memcpy(dst, buf + idx, len - idx); + return len - idx; +} - template - simdjson_inline void buf_block_reader::advance() { - idx += STEP_SIZE; - } +template +simdjson_inline void buf_block_reader::advance() { + idx += STEP_SIZE; +} - } // namespace stage1 - } // unnamed namespace - } // namespace icelake +} // namespace stage1 +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_BUF_BLOCK_READER_H @@ -23989,145 +25563,145 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { - namespace { - namespace stage1 { +namespace icelake { +namespace { +namespace stage1 { - /** - * Scans for escape characters in JSON, taking care with multiple backslashes (\\n vs. \n). - */ - struct json_escape_scanner { - /** The actual escape characters (the backslashes themselves). */ - uint64_t next_is_escaped = 0ULL; +/** + * Scans for escape characters in JSON, taking care with multiple backslashes (\\n vs. \n). + */ +struct json_escape_scanner { + /** The actual escape characters (the backslashes themselves). */ + uint64_t next_is_escaped = 0ULL; - struct escaped_and_escape { - /** - * Mask of escaped characters. - * - * ``` - * \n \\n \\\n \\\\n \ - * 0100100010100101000 - * n \ \ n \ \ - * ``` - */ - uint64_t escaped; - /** - * Mask of escape characters. - * - * ``` - * \n \\n \\\n \\\\n \ - * 1001000101001010001 - * \ \ \ \ \ \ \ - * ``` - */ - uint64_t escape; - }; + struct escaped_and_escape { + /** + * Mask of escaped characters. + * + * ``` + * \n \\n \\\n \\\\n \ + * 0100100010100101000 + * n \ \ n \ \ + * ``` + */ + uint64_t escaped; + /** + * Mask of escape characters. + * + * ``` + * \n \\n \\\n \\\\n \ + * 1001000101001010001 + * \ \ \ \ \ \ \ + * ``` + */ + uint64_t escape; + }; - /** - * Get a mask of both escape and escaped characters (the characters following a backslash). - * - * @param potential_escape A mask of the character that can escape others (but could be - * escaped itself). e.g. block.eq('\\') - */ - simdjson_really_inline escaped_and_escape next(uint64_t backslash) noexcept { + /** + * Get a mask of both escape and escaped characters (the characters following a backslash). + * + * @param potential_escape A mask of the character that can escape others (but could be + * escaped itself). e.g. block.eq('\\') + */ + simdjson_really_inline escaped_and_escape next(uint64_t backslash) noexcept { #if !SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT - if (!backslash) { return { next_escaped_without_backslashes(), 0 }; } + if (!backslash) { return {next_escaped_without_backslashes(), 0}; } #endif - // | | Mask (shows characters instead of 1's) | Depth | Instructions | - // |--------------------------------|----------------------------------------|-------|---------------------| - // | string | `\\n_\\\n___\\\n___\\\\___\\\\__\\\` | | | - // | | ` even odd even odd odd` | | | - // | potential_escape | ` \ \\\ \\\ \\\\ \\\\ \\\` | 1 | 1 (backslash & ~first_is_escaped) - // | escape_and_terminal_code | ` \n \ \n \ \n \ \ \ \ \ \` | 5 | 5 (next_escape_and_terminal_code()) - // | escaped | `\ \ n \ n \ \ \ \ \ ` X | 6 | 7 (escape_and_terminal_code ^ (potential_escape | first_is_escaped)) - // | escape | ` \ \ \ \ \ \ \ \ \ \` | 6 | 8 (escape_and_terminal_code & backslash) - // | first_is_escaped | `\ ` | 7 (*) | 9 (escape >> 63) () - // (*) this is not needed until the next iteration - uint64_t escape_and_terminal_code = next_escape_and_terminal_code(backslash & ~this->next_is_escaped); - uint64_t escaped = escape_and_terminal_code ^ (backslash | this->next_is_escaped); - uint64_t escape = escape_and_terminal_code & backslash; - this->next_is_escaped = escape >> 63; - return { escaped, escape }; - } + // | | Mask (shows characters instead of 1's) | Depth | Instructions | + // |--------------------------------|----------------------------------------|-------|---------------------| + // | string | `\\n_\\\n___\\\n___\\\\___\\\\__\\\` | | | + // | | ` even odd even odd odd` | | | + // | potential_escape | ` \ \\\ \\\ \\\\ \\\\ \\\` | 1 | 1 (backslash & ~first_is_escaped) + // | escape_and_terminal_code | ` \n \ \n \ \n \ \ \ \ \ \` | 5 | 5 (next_escape_and_terminal_code()) + // | escaped | `\ \ n \ n \ \ \ \ \ ` X | 6 | 7 (escape_and_terminal_code ^ (potential_escape | first_is_escaped)) + // | escape | ` \ \ \ \ \ \ \ \ \ \` | 6 | 8 (escape_and_terminal_code & backslash) + // | first_is_escaped | `\ ` | 7 (*) | 9 (escape >> 63) () + // (*) this is not needed until the next iteration + uint64_t escape_and_terminal_code = next_escape_and_terminal_code(backslash & ~this->next_is_escaped); + uint64_t escaped = escape_and_terminal_code ^ (backslash | this->next_is_escaped); + uint64_t escape = escape_and_terminal_code & backslash; + this->next_is_escaped = escape >> 63; + return {escaped, escape}; + } - private: - static constexpr const uint64_t ODD_BITS = 0xAAAAAAAAAAAAAAAAULL; +private: + static constexpr const uint64_t ODD_BITS = 0xAAAAAAAAAAAAAAAAULL; - simdjson_really_inline uint64_t next_escaped_without_backslashes() noexcept { - uint64_t escaped = this->next_is_escaped; - this->next_is_escaped = 0; - return escaped; - } + simdjson_really_inline uint64_t next_escaped_without_backslashes() noexcept { + uint64_t escaped = this->next_is_escaped; + this->next_is_escaped = 0; + return escaped; + } - /** - * Returns a mask of the next escape characters (masking out escaped backslashes), along with - * any non-backslash escape codes. - * - * \n \\n \\\n \\\\n returns: - * \n \ \ \n \ \ - * 11 100 1011 10100 - * - * You are expected to mask out the first bit yourself if the previous block had a trailing - * escape. - * - * & the result with potential_escape to get just the escape characters. - * ^ the result with (potential_escape | first_is_escaped) to get escaped characters. - */ - static simdjson_really_inline uint64_t next_escape_and_terminal_code(uint64_t potential_escape) noexcept { - // If we were to just shift and mask out any odd bits, we'd actually get a *half* right answer: - // any even-aligned backslash runs would be correct! Odd-aligned backslash runs would be - // inverted (\\\ would be 010 instead of 101). - // - // ``` - // string: | ____\\\\_\\\\_____ | - // maybe_escaped | ODD | \ \ \ \ | - // even-aligned ^^^ ^^^^ odd-aligned - // ``` - // - // Taking that into account, our basic strategy is: - // - // 1. Use subtraction to produce a mask with 1's for even-aligned runs and 0's for - // odd-aligned runs. - // 2. XOR all odd bits, which masks out the odd bits in even-aligned runs, and brings IN the - // odd bits in odd-aligned runs. - // 3. & with backslash to clean up any stray bits. - // runs are set to 0, and then XORing with "odd": - // - // | | Mask (shows characters instead of 1's) | Instructions | - // |--------------------------------|----------------------------------------|---------------------| - // | string | `\\n_\\\n___\\\n___\\\\___\\\\__\\\` | - // | | ` even odd even odd odd` | - // | maybe_escaped | ` n \\n \\n \\\_ \\\_ \\` X | 1 (potential_escape << 1) - // | maybe_escaped_and_odd | ` \n_ \\n _ \\\n_ _ \\\__ _\\\_ \\\` | 1 (maybe_escaped | odd) - // | even_series_codes_and_odd | ` n_\\\ _ n_ _\\\\ _ _ ` | 1 (maybe_escaped_and_odd - potential_escape) - // | escape_and_terminal_code | ` \n \ \n \ \n \ \ \ \ \ \` | 1 (^ odd) - // + /** + * Returns a mask of the next escape characters (masking out escaped backslashes), along with + * any non-backslash escape codes. + * + * \n \\n \\\n \\\\n returns: + * \n \ \ \n \ \ + * 11 100 1011 10100 + * + * You are expected to mask out the first bit yourself if the previous block had a trailing + * escape. + * + * & the result with potential_escape to get just the escape characters. + * ^ the result with (potential_escape | first_is_escaped) to get escaped characters. + */ + static simdjson_really_inline uint64_t next_escape_and_terminal_code(uint64_t potential_escape) noexcept { + // If we were to just shift and mask out any odd bits, we'd actually get a *half* right answer: + // any even-aligned backslash runs would be correct! Odd-aligned backslash runs would be + // inverted (\\\ would be 010 instead of 101). + // + // ``` + // string: | ____\\\\_\\\\_____ | + // maybe_escaped | ODD | \ \ \ \ | + // even-aligned ^^^ ^^^^ odd-aligned + // ``` + // + // Taking that into account, our basic strategy is: + // + // 1. Use subtraction to produce a mask with 1's for even-aligned runs and 0's for + // odd-aligned runs. + // 2. XOR all odd bits, which masks out the odd bits in even-aligned runs, and brings IN the + // odd bits in odd-aligned runs. + // 3. & with backslash to clean up any stray bits. + // runs are set to 0, and then XORing with "odd": + // + // | | Mask (shows characters instead of 1's) | Instructions | + // |--------------------------------|----------------------------------------|---------------------| + // | string | `\\n_\\\n___\\\n___\\\\___\\\\__\\\` | + // | | ` even odd even odd odd` | + // | maybe_escaped | ` n \\n \\n \\\_ \\\_ \\` X | 1 (potential_escape << 1) + // | maybe_escaped_and_odd | ` \n_ \\n _ \\\n_ _ \\\__ _\\\_ \\\` | 1 (maybe_escaped | odd) + // | even_series_codes_and_odd | ` n_\\\ _ n_ _\\\\ _ _ ` | 1 (maybe_escaped_and_odd - potential_escape) + // | escape_and_terminal_code | ` \n \ \n \ \n \ \ \ \ \ \` | 1 (^ odd) + // - // Escaped characters are characters following an escape. - uint64_t maybe_escaped = potential_escape << 1; + // Escaped characters are characters following an escape. + uint64_t maybe_escaped = potential_escape << 1; - // To distinguish odd from even escape sequences, therefore, we turn on any *starting* - // escapes that are on an odd byte. (We actually bring in all odd bits, for speed.) - // - Odd runs of backslashes are 0000, and the code at the end ("n" in \n or \\n) is 1. - // - Odd runs of backslashes are 1111, and the code at the end ("n" in \n or \\n) is 0. - // - All other odd bytes are 1, and even bytes are 0. - uint64_t maybe_escaped_and_odd_bits = maybe_escaped | ODD_BITS; - uint64_t even_series_codes_and_odd_bits = maybe_escaped_and_odd_bits - potential_escape; + // To distinguish odd from even escape sequences, therefore, we turn on any *starting* + // escapes that are on an odd byte. (We actually bring in all odd bits, for speed.) + // - Odd runs of backslashes are 0000, and the code at the end ("n" in \n or \\n) is 1. + // - Odd runs of backslashes are 1111, and the code at the end ("n" in \n or \\n) is 0. + // - All other odd bytes are 1, and even bytes are 0. + uint64_t maybe_escaped_and_odd_bits = maybe_escaped | ODD_BITS; + uint64_t even_series_codes_and_odd_bits = maybe_escaped_and_odd_bits - potential_escape; - // Now we flip all odd bytes back with xor. This: - // - Makes odd runs of backslashes go from 0000 to 1010 - // - Makes even runs of backslashes go from 1111 to 1010 - // - Sets actually-escaped codes to 1 (the n in \n and \\n: \n = 11, \\n = 100) - // - Resets all other bytes to 0 - return even_series_codes_and_odd_bits ^ ODD_BITS; - } - }; + // Now we flip all odd bytes back with xor. This: + // - Makes odd runs of backslashes go from 0000 to 1010 + // - Makes even runs of backslashes go from 1111 to 1010 + // - Sets actually-escaped codes to 1 (the n in \n and \\n: \n = 11, \\n = 100) + // - Resets all other bytes to 0 + return even_series_codes_and_odd_bits ^ ODD_BITS; + } +}; - } // namespace stage1 - } // unnamed namespace - } // namespace icelake +} // namespace stage1 +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRING_SCANNER_H @@ -24143,93 +25717,93 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { - namespace { - namespace stage1 { +namespace icelake { +namespace { +namespace stage1 { - struct json_string_block { - // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 - simdjson_really_inline json_string_block(uint64_t escaped, uint64_t quote, uint64_t in_string) : - _escaped(escaped), _quote(quote), _in_string(in_string) {} +struct json_string_block { + // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 + simdjson_really_inline json_string_block(uint64_t escaped, uint64_t quote, uint64_t in_string) : + _escaped(escaped), _quote(quote), _in_string(in_string) {} - // Escaped characters (characters following an escape() character) - simdjson_really_inline uint64_t escaped() const { return _escaped; } - // Real (non-backslashed) quotes - simdjson_really_inline uint64_t quote() const { return _quote; } - // Only characters inside the string (not including the quotes) - simdjson_really_inline uint64_t string_content() const { return _in_string & ~_quote; } - // Return a mask of whether the given characters are inside a string (only works on non-quotes) - simdjson_really_inline uint64_t non_quote_inside_string(uint64_t mask) const { return mask & _in_string; } - // Return a mask of whether the given characters are inside a string (only works on non-quotes) - simdjson_really_inline uint64_t non_quote_outside_string(uint64_t mask) const { return mask & ~_in_string; } - // Tail of string (everything except the start quote) - simdjson_really_inline uint64_t string_tail() const { return _in_string ^ _quote; } + // Escaped characters (characters following an escape() character) + simdjson_really_inline uint64_t escaped() const { return _escaped; } + // Real (non-backslashed) quotes + simdjson_really_inline uint64_t quote() const { return _quote; } + // Only characters inside the string (not including the quotes) + simdjson_really_inline uint64_t string_content() const { return _in_string & ~_quote; } + // Return a mask of whether the given characters are inside a string (only works on non-quotes) + simdjson_really_inline uint64_t non_quote_inside_string(uint64_t mask) const { return mask & _in_string; } + // Return a mask of whether the given characters are inside a string (only works on non-quotes) + simdjson_really_inline uint64_t non_quote_outside_string(uint64_t mask) const { return mask & ~_in_string; } + // Tail of string (everything except the start quote) + simdjson_really_inline uint64_t string_tail() const { return _in_string ^ _quote; } - // escaped characters (backslashed--does not include the hex characters after \u) - uint64_t _escaped; - // real quotes (non-escaped ones) - uint64_t _quote; - // string characters (includes start quote but not end quote) - uint64_t _in_string; - }; + // escaped characters (backslashed--does not include the hex characters after \u) + uint64_t _escaped; + // real quotes (non-escaped ones) + uint64_t _quote; + // string characters (includes start quote but not end quote) + uint64_t _in_string; +}; - // Scans blocks for string characters, storing the state necessary to do so - class json_string_scanner { - public: - simdjson_really_inline json_string_block next(const simd::simd8x64& in); - // Returns either UNCLOSED_STRING or SUCCESS - simdjson_really_inline error_code finish(); +// Scans blocks for string characters, storing the state necessary to do so +class json_string_scanner { +public: + simdjson_really_inline json_string_block next(const simd::simd8x64& in); + // Returns either UNCLOSED_STRING or SUCCESS + simdjson_really_inline error_code finish(); - private: - // Scans for escape characters - json_escape_scanner escape_scanner{}; - // Whether the last iteration was still inside a string (all 1's = true, all 0's = false). - uint64_t prev_in_string = 0ULL; - }; +private: + // Scans for escape characters + json_escape_scanner escape_scanner{}; + // Whether the last iteration was still inside a string (all 1's = true, all 0's = false). + uint64_t prev_in_string = 0ULL; +}; - // - // Return a mask of all string characters plus end quotes. - // - // prev_escaped is overflow saying whether the next character is escaped. - // prev_in_string is overflow saying whether we're still in a string. - // - // Backslash sequences outside of quotes will be detected in stage 2. - // - simdjson_really_inline json_string_block json_string_scanner::next(const simd::simd8x64& in) { - const uint64_t backslash = in.eq('\\'); - const uint64_t escaped = escape_scanner.next(backslash).escaped; - const uint64_t quote = in.eq('"') & ~escaped; +// +// Return a mask of all string characters plus end quotes. +// +// prev_escaped is overflow saying whether the next character is escaped. +// prev_in_string is overflow saying whether we're still in a string. +// +// Backslash sequences outside of quotes will be detected in stage 2. +// +simdjson_really_inline json_string_block json_string_scanner::next(const simd::simd8x64& in) { + const uint64_t backslash = in.eq('\\'); + const uint64_t escaped = escape_scanner.next(backslash).escaped; + const uint64_t quote = in.eq('"') & ~escaped; - // - // prefix_xor flips on bits inside the string (and flips off the end quote). - // - // Then we xor with prev_in_string: if we were in a string already, its effect is flipped - // (characters inside strings are outside, and characters outside strings are inside). - // - const uint64_t in_string = prefix_xor(quote) ^ prev_in_string; + // + // prefix_xor flips on bits inside the string (and flips off the end quote). + // + // Then we xor with prev_in_string: if we were in a string already, its effect is flipped + // (characters inside strings are outside, and characters outside strings are inside). + // + const uint64_t in_string = prefix_xor(quote) ^ prev_in_string; - // - // Check if we're still in a string at the end of the box so the next block will know - // - prev_in_string = uint64_t(static_cast(in_string) >> 63); + // + // Check if we're still in a string at the end of the box so the next block will know + // + prev_in_string = uint64_t(static_cast(in_string) >> 63); - // Use ^ to turn the beginning quote off, and the end quote on. + // Use ^ to turn the beginning quote off, and the end quote on. - // We are returning a function-local object so either we get a move constructor - // or we get copy elision. - return json_string_block(escaped, quote, in_string); - } + // We are returning a function-local object so either we get a move constructor + // or we get copy elision. + return json_string_block(escaped, quote, in_string); +} - simdjson_really_inline error_code json_string_scanner::finish() { - if (prev_in_string) { - return UNCLOSED_STRING; - } - return SUCCESS; - } +simdjson_really_inline error_code json_string_scanner::finish() { + if (prev_in_string) { + return UNCLOSED_STRING; + } + return SUCCESS; +} - } // namespace stage1 - } // unnamed namespace - } // namespace icelake +} // namespace stage1 +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRING_SCANNER_H @@ -24245,206 +25819,203 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { - namespace { - namespace utf8_validation { +namespace icelake { +namespace { +namespace utf8_validation { - using namespace simd; +using namespace simd; - simdjson_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { - // Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) - // Bit 1 = Too Long (ASCII followed by continuation) - // Bit 2 = Overlong 3-byte - // Bit 4 = Surrogate - // Bit 5 = Overlong 2-byte - // Bit 7 = Two Continuations - constexpr const uint8_t TOO_SHORT = 1 << 0; // 11______ 0_______ - // 11______ 11______ - constexpr const uint8_t TOO_LONG = 1 << 1; // 0_______ 10______ - constexpr const uint8_t OVERLONG_3 = 1 << 2; // 11100000 100_____ - constexpr const uint8_t SURROGATE = 1 << 4; // 11101101 101_____ - constexpr const uint8_t OVERLONG_2 = 1 << 5; // 1100000_ 10______ - constexpr const uint8_t TWO_CONTS = 1 << 7; // 10______ 10______ - constexpr const uint8_t TOO_LARGE = 1 << 3; // 11110100 1001____ - // 11110100 101_____ - // 11110101 1001____ - // 11110101 101_____ - // 1111011_ 1001____ - // 1111011_ 101_____ - // 11111___ 1001____ - // 11111___ 101_____ - constexpr const uint8_t TOO_LARGE_1000 = 1 << 6; - // 11110101 1000____ - // 1111011_ 1000____ - // 11111___ 1000____ - constexpr const uint8_t OVERLONG_4 = 1 << 6; // 11110000 1000____ + simdjson_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { +// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) +// Bit 1 = Too Long (ASCII followed by continuation) +// Bit 2 = Overlong 3-byte +// Bit 4 = Surrogate +// Bit 5 = Overlong 2-byte +// Bit 7 = Two Continuations + constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______ + // 11______ 11______ + constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______ + constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____ + constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____ + constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______ + constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______ + constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____ + // 11110100 101_____ + // 11110101 1001____ + // 11110101 101_____ + // 1111011_ 1001____ + // 1111011_ 101_____ + // 11111___ 1001____ + // 11111___ 101_____ + constexpr const uint8_t TOO_LARGE_1000 = 1<<6; + // 11110101 1000____ + // 1111011_ 1000____ + // 11111___ 1000____ + constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____ - const simd8 byte_1_high = prev1.shr<4>().lookup_16( - // 0_______ ________ - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - // 10______ ________ - TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, - // 1100____ ________ - TOO_SHORT | OVERLONG_2, - // 1101____ ________ - TOO_SHORT, - // 1110____ ________ - TOO_SHORT | OVERLONG_3 | SURROGATE, - // 1111____ ________ - TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 - ); - constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . - const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( - // ____0000 ________ - CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, - // ____0001 ________ - CARRY | OVERLONG_2, - // ____001_ ________ - CARRY, - CARRY, + const simd8 byte_1_high = prev1.shr<4>().lookup_16( + // 0_______ ________ + TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, + TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, + // 10______ ________ + TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, + // 1100____ ________ + TOO_SHORT | OVERLONG_2, + // 1101____ ________ + TOO_SHORT, + // 1110____ ________ + TOO_SHORT | OVERLONG_3 | SURROGATE, + // 1111____ ________ + TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 + ); + constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . + const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( + // ____0000 ________ + CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, + // ____0001 ________ + CARRY | OVERLONG_2, + // ____001_ ________ + CARRY, + CARRY, - // ____0100 ________ - CARRY | TOO_LARGE, - // ____0101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____011_ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, + // ____0100 ________ + CARRY | TOO_LARGE, + // ____0101 ________ + CARRY | TOO_LARGE | TOO_LARGE_1000, + // ____011_ ________ + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____1___ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____1101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000 - ); - const simd8 byte_2_high = input.shr<4>().lookup_16( - // ________ 0_______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, + // ____1___ ________ + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + // ____1101 ________ + CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000 + ); + const simd8 byte_2_high = input.shr<4>().lookup_16( + // ________ 0_______ + TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, + TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - // ________ 1000____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, - // ________ 1001____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, - // ________ 101_____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, + // ________ 1000____ + TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, + // ________ 1001____ + TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, + // ________ 101_____ + TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, + TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - // ________ 11______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT - ); - return (byte_1_high & byte_1_low & byte_2_high); - } - simdjson_inline simd8 check_multibyte_lengths(const simd8 input, - const simd8 prev_input, const simd8 sc) { - simd8 prev2 = input.prev<2>(prev_input); - simd8 prev3 = input.prev<3>(prev_input); - simd8 must23 = must_be_2_3_continuation(prev2, prev3); - simd8 must23_80 = must23 & uint8_t(0x80); - return must23_80 ^ sc; - } + // ________ 11______ + TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT + ); + return (byte_1_high & byte_1_low & byte_2_high); + } + simdjson_inline simd8 check_multibyte_lengths(const simd8 input, + const simd8 prev_input, const simd8 sc) { + simd8 prev2 = input.prev<2>(prev_input); + simd8 prev3 = input.prev<3>(prev_input); + simd8 must23 = must_be_2_3_continuation(prev2, prev3); + simd8 must23_80 = must23 & uint8_t(0x80); + return must23_80 ^ sc; + } - // - // Return nonzero if there are incomplete multibyte characters at the end of the block: - // e.g. if there is a 4-byte character, but it's 3 bytes from the end. - // - simdjson_inline simd8 is_incomplete(const simd8 input) { - // If the previous input's last 3 bytes match this, they're too short (they ended at EOF): - // ... 1111____ 111_____ 11______ -#if SIMDJSON_IMPLEMENTATION_ICELAKE - static const uint8_t max_array[64] = { - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0xf0u - 1, 0xe0u - 1, 0xc0u - 1 - }; + // + // Return nonzero if there are incomplete multibyte characters at the end of the block: + // e.g. if there is a 4-byte character, but it's 3 bytes from the end. + // + simdjson_inline simd8 is_incomplete(const simd8 input) { + // If the previous input's last 3 bytes match this, they're too short (they ended at EOF): + // ... 1111____ 111_____ 11______ +#if SIMDJSON_IMPLEMENTATION_ICELAKE || (SIMDJSON_IMPLEMENTATION_RVV_VLS && __riscv_v_fixed_vlen >= 512) + static const uint8_t max_array[64] = { + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0xf0u-1, 0xe0u-1, 0xc0u-1 + }; #else - static const uint8_t max_array[32] = { - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0xf0u - 1, 0xe0u - 1, 0xc0u - 1 - }; + static const uint8_t max_array[32] = { + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0xf0u-1, 0xe0u-1, 0xc0u-1 + }; #endif - const simd8 max_value(&max_array[sizeof(max_array) - sizeof(simd8)]); - return input.gt_bits(max_value); - } + const simd8 max_value(&max_array[sizeof(max_array)-sizeof(simd8)]); + return input.gt_bits(max_value); + } - struct utf8_checker { - // If this is nonzero, there has been a UTF-8 error. - simd8 error; - // The last input we received - simd8 prev_input_block; - // Whether the last input we received was incomplete (used for ASCII fast path) - simd8 prev_incomplete; + struct utf8_checker { + // If this is nonzero, there has been a UTF-8 error. + simd8 error; + // The last input we received + simd8 prev_input_block; + // Whether the last input we received was incomplete (used for ASCII fast path) + simd8 prev_incomplete; - // - // Check whether the current bytes are valid UTF-8. - // - simdjson_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { - // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes - // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) - simd8 prev1 = input.prev<1>(prev_input); - simd8 sc = check_special_cases(input, prev1); - this->error |= check_multibyte_lengths(input, prev_input, sc); - } + // + // Check whether the current bytes are valid UTF-8. + // + simdjson_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { + // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes + // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) + simd8 prev1 = input.prev<1>(prev_input); + simd8 sc = check_special_cases(input, prev1); + this->error |= check_multibyte_lengths(input, prev_input, sc); + } - // The only problem that can happen at EOF is that a multibyte character is too short - // or a byte value too large in the last bytes: check_special_cases only checks for bytes - // too large in the first of two bytes. - simdjson_inline void check_eof() { - // If the previous block had incomplete UTF-8 characters at the end, an ASCII block can't - // possibly finish them. - this->error |= this->prev_incomplete; - } + // The only problem that can happen at EOF is that a multibyte character is too short + // or a byte value too large in the last bytes: check_special_cases only checks for bytes + // too large in the first of two bytes. + simdjson_inline void check_eof() { + // If the previous block had incomplete UTF-8 characters at the end, an ASCII block can't + // possibly finish them. + this->error |= this->prev_incomplete; + } - simdjson_inline void check_next_input(const simd8x64& input) { - if (simdjson_likely(is_ascii(input))) { - this->error |= this->prev_incomplete; - } - else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 1) - || (simd8x64::NUM_CHUNKS == 2) - || (simd8x64::NUM_CHUNKS == 4), - "We support one, two or four chunks per 64-byte block."); - SIMDJSON_IF_CONSTEXPR(simd8x64::NUM_CHUNKS == 1) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - } -else SIMDJSON_IF_CONSTEXPR(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); + simdjson_inline void check_next_input(const simd8x64& input) { + if(simdjson_likely(is_ascii(input))) { + this->error |= this->prev_incomplete; + } else { + // you might think that a for-loop would work, but under Visual Studio, it is not good enough. + static_assert((simd8x64::NUM_CHUNKS == 1) + ||(simd8x64::NUM_CHUNKS == 2) + || (simd8x64::NUM_CHUNKS == 4), + "We support one, two or four chunks per 64-byte block."); + SIMDJSON_IF_CONSTEXPR (simd8x64::NUM_CHUNKS == 1) { + this->check_utf8_bytes(input.get<0>(), this->prev_input_block); + } else SIMDJSON_IF_CONSTEXPR (simd8x64::NUM_CHUNKS == 2) { + this->check_utf8_bytes(input.get<0>(), this->prev_input_block); + this->check_utf8_bytes(input.get<1>(), input.get<0>()); + } else SIMDJSON_IF_CONSTEXPR (simd8x64::NUM_CHUNKS == 4) { + this->check_utf8_bytes(input.get<0>(), this->prev_input_block); + this->check_utf8_bytes(input.get<1>(), input.get<0>()); + this->check_utf8_bytes(input.get<2>(), input.get<1>()); + this->check_utf8_bytes(input.get<3>(), input.get<2>()); } - else SIMDJSON_IF_CONSTEXPR(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - this->prev_incomplete = is_incomplete(input.chunks[simd8x64::NUM_CHUNKS - 1]); - this->prev_input_block = input.chunks[simd8x64::NUM_CHUNKS - 1]; - } - } - // do not forget to call check_eof! - simdjson_inline error_code errors() { - return this->error.any_bits_set_anywhere() ? error_code::UTF8_ERROR : error_code::SUCCESS; - } + this->prev_incomplete = is_incomplete(input.get::NUM_CHUNKS-1>()); + this->prev_input_block = input.get::NUM_CHUNKS-1>(); + } + } + // do not forget to call check_eof! + simdjson_warn_unused simdjson_inline error_code errors() { + return this->error.any_bits_set_anywhere() ? error_code::UTF8_ERROR : error_code::SUCCESS; + } - }; // struct utf8_checker - } // namespace utf8_validation + }; // struct utf8_checker +} // namespace utf8_validation - } // unnamed namespace - } // namespace icelake +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_UTF8_LOOKUP4_ALGORITHM_H @@ -24461,161 +26032,161 @@ else SIMDJSON_IF_CONSTEXPR(simd8x64::NUM_CHUNKS == 2) { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { - namespace { - namespace stage1 { +namespace icelake { +namespace { +namespace stage1 { - /** - * A block of scanned json, with information on operators and scalars. - * - * We seek to identify pseudo-structural characters. Anything that is inside - * a string must be omitted (hence & ~_string.string_tail()). - * Otherwise, pseudo-structural characters come in two forms. - * 1. We have the structural characters ([,],{,},:, comma). The - * term 'structural character' is from the JSON RFC. - * 2. We have the 'scalar pseudo-structural characters'. - * Scalars are quotes, and any character except structural characters and white space. - * - * To identify the scalar pseudo-structural characters, we must look at what comes - * before them: it must be a space, a quote or a structural characters. - * Starting with simdjson v0.3, we identify them by - * negation: we identify everything that is followed by a non-quote scalar, - * and we negate that. Whatever remains must be a 'scalar pseudo-structural character'. - */ - struct json_block { - public: - // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 - simdjson_inline json_block(json_string_block&& string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : - _string(std::move(string)), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} - simdjson_inline json_block(json_string_block string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : - _string(string), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} +/** + * A block of scanned json, with information on operators and scalars. + * + * We seek to identify pseudo-structural characters. Anything that is inside + * a string must be omitted (hence & ~_string.string_tail()). + * Otherwise, pseudo-structural characters come in two forms. + * 1. We have the structural characters ([,],{,},:, comma). The + * term 'structural character' is from the JSON RFC. + * 2. We have the 'scalar pseudo-structural characters'. + * Scalars are quotes, and any character except structural characters and white space. + * + * To identify the scalar pseudo-structural characters, we must look at what comes + * before them: it must be a space, a quote or a structural characters. + * Starting with simdjson v0.3, we identify them by + * negation: we identify everything that is followed by a non-quote scalar, + * and we negate that. Whatever remains must be a 'scalar pseudo-structural character'. + */ +struct json_block { +public: + // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 + simdjson_inline json_block(json_string_block&& string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : + _string(std::move(string)), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} + simdjson_inline json_block(json_string_block string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : + _string(string), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} - /** - * The start of structurals. - * In simdjson prior to v0.3, these were called the pseudo-structural characters. - **/ - simdjson_inline uint64_t structural_start() const noexcept { return potential_structural_start() & ~_string.string_tail(); } - /** All JSON whitespace (i.e. not in a string) */ - simdjson_inline uint64_t whitespace() const noexcept { return non_quote_outside_string(_characters.whitespace()); } + /** + * The start of structurals. + * In simdjson prior to v0.3, these were called the pseudo-structural characters. + **/ + simdjson_inline uint64_t structural_start() const noexcept { return potential_structural_start() & ~_string.string_tail(); } + /** All JSON whitespace (i.e. not in a string) */ + simdjson_inline uint64_t whitespace() const noexcept { return non_quote_outside_string(_characters.whitespace()); } - // Helpers + // Helpers - /** Whether the given characters are inside a string (only works on non-quotes) */ - simdjson_inline uint64_t non_quote_inside_string(uint64_t mask) const noexcept { return _string.non_quote_inside_string(mask); } - /** Whether the given characters are outside a string (only works on non-quotes) */ - simdjson_inline uint64_t non_quote_outside_string(uint64_t mask) const noexcept { return _string.non_quote_outside_string(mask); } + /** Whether the given characters are inside a string (only works on non-quotes) */ + simdjson_inline uint64_t non_quote_inside_string(uint64_t mask) const noexcept { return _string.non_quote_inside_string(mask); } + /** Whether the given characters are outside a string (only works on non-quotes) */ + simdjson_inline uint64_t non_quote_outside_string(uint64_t mask) const noexcept { return _string.non_quote_outside_string(mask); } - // string and escape characters - json_string_block _string; - // whitespace, structural characters ('operators'), scalars - json_character_block _characters; - // whether the previous character was a scalar - uint64_t _follows_potential_nonquote_scalar; - private: - // Potential structurals (i.e. disregarding strings) + // string and escape characters + json_string_block _string; + // whitespace, structural characters ('operators'), scalars + json_character_block _characters; + // whether the previous character was a scalar + uint64_t _follows_potential_nonquote_scalar; +private: + // Potential structurals (i.e. disregarding strings) - /** - * structural elements ([,],{,},:, comma) plus scalar starts like 123, true and "abc". - * They may reside inside a string. - **/ - simdjson_inline uint64_t potential_structural_start() const noexcept { return _characters.op() | potential_scalar_start(); } - /** - * The start of non-operator runs, like 123, true and "abc". - * It main reside inside a string. - **/ - simdjson_inline uint64_t potential_scalar_start() const noexcept { - // The term "scalar" refers to anything except structural characters and white space - // (so letters, numbers, quotes). - // Whenever it is preceded by something that is not a structural element ({,},[,],:, ") nor a white-space - // then we know that it is irrelevant structurally. - return _characters.scalar() & ~follows_potential_scalar(); - } - /** - * Whether the given character is immediately after a non-operator like 123, true. - * The characters following a quote are not included. - */ - simdjson_inline uint64_t follows_potential_scalar() const noexcept { - // _follows_potential_nonquote_scalar: is defined as marking any character that follows a character - // that is not a structural element ({,},[,],:, comma) nor a quote (") and that is not a - // white space. - // It is understood that within quoted region, anything at all could be marked (irrelevant). - return _follows_potential_nonquote_scalar; - } - }; + /** + * structural elements ([,],{,},:, comma) plus scalar starts like 123, true and "abc". + * They may reside inside a string. + **/ + simdjson_inline uint64_t potential_structural_start() const noexcept { return _characters.op() | potential_scalar_start(); } + /** + * The start of non-operator runs, like 123, true and "abc". + * It main reside inside a string. + **/ + simdjson_inline uint64_t potential_scalar_start() const noexcept { + // The term "scalar" refers to anything except structural characters and white space + // (so letters, numbers, quotes). + // Whenever it is preceded by something that is not a structural element ({,},[,],:, ") nor a white-space + // then we know that it is irrelevant structurally. + return _characters.scalar() & ~follows_potential_scalar(); + } + /** + * Whether the given character is immediately after a non-operator like 123, true. + * The characters following a quote are not included. + */ + simdjson_inline uint64_t follows_potential_scalar() const noexcept { + // _follows_potential_nonquote_scalar: is defined as marking any character that follows a character + // that is not a structural element ({,},[,],:, comma) nor a quote (") and that is not a + // white space. + // It is understood that within quoted region, anything at all could be marked (irrelevant). + return _follows_potential_nonquote_scalar; + } +}; - /** - * Scans JSON for important bits: structural characters or 'operators', strings, and scalars. - * - * The scanner starts by calculating two distinct things: - * - string characters (taking \" into account) - * - structural characters or 'operators' ([]{},:, comma) - * and scalars (runs of non-operators like 123, true and "abc") - * - * To minimize data dependency (a key component of the scanner's speed), it finds these in parallel: - * in particular, the operator/scalar bit will find plenty of things that are actually part of - * strings. When we're done, json_block will fuse the two together by masking out tokens that are - * part of a string. - */ - class json_scanner { - public: - json_scanner() = default; - simdjson_inline json_block next(const simd::simd8x64& in); - // Returns either UNCLOSED_STRING or SUCCESS - simdjson_inline error_code finish(); +/** + * Scans JSON for important bits: structural characters or 'operators', strings, and scalars. + * + * The scanner starts by calculating two distinct things: + * - string characters (taking \" into account) + * - structural characters or 'operators' ([]{},:, comma) + * and scalars (runs of non-operators like 123, true and "abc") + * + * To minimize data dependency (a key component of the scanner's speed), it finds these in parallel: + * in particular, the operator/scalar bit will find plenty of things that are actually part of + * strings. When we're done, json_block will fuse the two together by masking out tokens that are + * part of a string. + */ +class json_scanner { +public: + json_scanner() = default; + simdjson_inline json_block next(const simd::simd8x64& in); + // Returns either UNCLOSED_STRING or SUCCESS + simdjson_warn_unused simdjson_inline error_code finish(); - private: - // Whether the last character of the previous iteration is part of a scalar token - // (anything except whitespace or a structural character/'operator'). - uint64_t prev_scalar = 0ULL; - json_string_scanner string_scanner{}; - }; +private: + // Whether the last character of the previous iteration is part of a scalar token + // (anything except whitespace or a structural character/'operator'). + uint64_t prev_scalar = 0ULL; + json_string_scanner string_scanner{}; +}; - // - // Check if the current character immediately follows a matching character. - // - // For example, this checks for quotes with backslashes in front of them: - // - // const uint64_t backslashed_quote = in.eq('"') & immediately_follows(in.eq('\'), prev_backslash); - // - simdjson_inline uint64_t follows(const uint64_t match, uint64_t& overflow) { - const uint64_t result = match << 1 | overflow; - overflow = match >> 63; - return result; - } +// +// Check if the current character immediately follows a matching character. +// +// For example, this checks for quotes with backslashes in front of them: +// +// const uint64_t backslashed_quote = in.eq('"') & immediately_follows(in.eq('\'), prev_backslash); +// +simdjson_inline uint64_t follows(const uint64_t match, uint64_t &overflow) { + const uint64_t result = match << 1 | overflow; + overflow = match >> 63; + return result; +} - simdjson_inline json_block json_scanner::next(const simd::simd8x64& in) { - json_string_block strings = string_scanner.next(in); - // identifies the white-space and the structural characters - json_character_block characters = json_character_block::classify(in); - // The term "scalar" refers to anything except structural characters and white space - // (so letters, numbers, quotes). - // We want follows_scalar to mark anything that follows a non-quote scalar (so letters and numbers). - // - // A terminal quote should either be followed by a structural character (comma, brace, bracket, colon) - // or nothing. However, we still want ' "a string"true ' to mark the 't' of 'true' as a potential - // pseudo-structural character just like we would if we had ' "a string" true '; otherwise we - // may need to add an extra check when parsing strings. - // - // Performance: there are many ways to skin this cat. - const uint64_t nonquote_scalar = characters.scalar() & ~strings.quote(); - uint64_t follows_nonquote_scalar = follows(nonquote_scalar, prev_scalar); - // We are returning a function-local object so either we get a move constructor - // or we get copy elision. - return json_block( - strings,// strings is a function-local object so either it moves or the copy is elided. - characters, - follows_nonquote_scalar - ); - } +simdjson_inline json_block json_scanner::next(const simd::simd8x64& in) { + json_string_block strings = string_scanner.next(in); + // identifies the white-space and the structural characters + json_character_block characters = json_character_block::classify(in); + // The term "scalar" refers to anything except structural characters and white space + // (so letters, numbers, quotes). + // We want follows_scalar to mark anything that follows a non-quote scalar (so letters and numbers). + // + // A terminal quote should either be followed by a structural character (comma, brace, bracket, colon) + // or nothing. However, we still want ' "a string"true ' to mark the 't' of 'true' as a potential + // pseudo-structural character just like we would if we had ' "a string" true '; otherwise we + // may need to add an extra check when parsing strings. + // + // Performance: there are many ways to skin this cat. + const uint64_t nonquote_scalar = characters.scalar() & ~strings.quote(); + uint64_t follows_nonquote_scalar = follows(nonquote_scalar, prev_scalar); + // We are returning a function-local object so either we get a move constructor + // or we get copy elision. + return json_block( + strings,// strings is a function-local object so either it moves or the copy is elided. + characters, + follows_nonquote_scalar + ); +} - simdjson_inline error_code json_scanner::finish() { - return string_scanner.finish(); - } +simdjson_warn_unused simdjson_inline error_code json_scanner::finish() { + return string_scanner.finish(); +} - } // namespace stage1 - } // unnamed namespace - } // namespace icelake +} // namespace stage1 +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_SCANNER_H @@ -24633,99 +26204,99 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { - namespace { - namespace stage1 { +namespace icelake { +namespace { +namespace stage1 { - /** - * This algorithm is used to quickly identify the last structural position that - * makes up a complete document. - * - * It does this by going backwards and finding the last *document boundary* (a - * place where one value follows another without a comma between them). If the - * last document (the characters after the boundary) has an equal number of - * start and end brackets, it is considered complete. - * - * Simply put, we iterate over the structural characters, starting from - * the end. We consider that we found the end of a JSON document when the - * first element of the pair is NOT one of these characters: '{' '[' ':' ',' - * and when the second element is NOT one of these characters: '}' ']' ':' ','. - * - * This simple comparison works most of the time, but it does not cover cases - * where the batch's structural indexes contain a perfect amount of documents. - * In such a case, we do not have access to the structural index which follows - * the last document, therefore, we do not have access to the second element in - * the pair, and that means we cannot identify the last document. To fix this - * issue, we keep a count of the open and closed curly/square braces we found - * while searching for the pair. When we find a pair AND the count of open and - * closed curly/square braces is the same, we know that we just passed a - * complete document, therefore the last json buffer location is the end of the - * batch. - */ - simdjson_inline uint32_t find_next_document_index(dom_parser_implementation& parser) { - // Variant: do not count separately, just figure out depth - if (parser.n_structural_indexes == 0) { return 0; } - auto arr_cnt = 0; - auto obj_cnt = 0; - for (auto i = parser.n_structural_indexes - 1; i > 0; i--) { - auto idxb = parser.structural_indexes[i]; - switch (parser.buf[idxb]) { - case ':': - case ',': - continue; - case '}': - obj_cnt--; - continue; - case ']': - arr_cnt--; - continue; - case '{': - obj_cnt++; - break; - case '[': - arr_cnt++; - break; - } - auto idxa = parser.structural_indexes[i - 1]; - switch (parser.buf[idxa]) { - case '{': - case '[': - case ':': - case ',': - continue; - } - // Last document is complete, so the next document will appear after! - if (!arr_cnt && !obj_cnt) { - return parser.n_structural_indexes; - } - // Last document is incomplete; mark the document at i + 1 as the next one - return i; - } - // If we made it to the end, we want to finish counting to see if we have a full document. - switch (parser.buf[parser.structural_indexes[0]]) { - case '}': - obj_cnt--; - break; - case ']': - arr_cnt--; - break; - case '{': - obj_cnt++; - break; - case '[': - arr_cnt++; - break; - } - if (!arr_cnt && !obj_cnt) { - // We have a complete document. - return parser.n_structural_indexes; - } - return 0; - } +/** + * This algorithm is used to quickly identify the last structural position that + * makes up a complete document. + * + * It does this by going backwards and finding the last *document boundary* (a + * place where one value follows another without a comma between them). If the + * last document (the characters after the boundary) has an equal number of + * start and end brackets, it is considered complete. + * + * Simply put, we iterate over the structural characters, starting from + * the end. We consider that we found the end of a JSON document when the + * first element of the pair is NOT one of these characters: '{' '[' ':' ',' + * and when the second element is NOT one of these characters: '}' ']' ':' ','. + * + * This simple comparison works most of the time, but it does not cover cases + * where the batch's structural indexes contain a perfect amount of documents. + * In such a case, we do not have access to the structural index which follows + * the last document, therefore, we do not have access to the second element in + * the pair, and that means we cannot identify the last document. To fix this + * issue, we keep a count of the open and closed curly/square braces we found + * while searching for the pair. When we find a pair AND the count of open and + * closed curly/square braces is the same, we know that we just passed a + * complete document, therefore the last json buffer location is the end of the + * batch. + */ +simdjson_inline uint32_t find_next_document_index(dom_parser_implementation &parser) { + // Variant: do not count separately, just figure out depth + if(parser.n_structural_indexes == 0) { return 0; } + auto arr_cnt = 0; + auto obj_cnt = 0; + for (auto i = parser.n_structural_indexes - 1; i > 0; i--) { + auto idxb = parser.structural_indexes[i]; + switch (parser.buf[idxb]) { + case ':': + case ',': + continue; + case '}': + obj_cnt--; + continue; + case ']': + arr_cnt--; + continue; + case '{': + obj_cnt++; + break; + case '[': + arr_cnt++; + break; + } + auto idxa = parser.structural_indexes[i - 1]; + switch (parser.buf[idxa]) { + case '{': + case '[': + case ':': + case ',': + continue; + } + // Last document is complete, so the next document will appear after! + if (!arr_cnt && !obj_cnt) { + return parser.n_structural_indexes; + } + // Last document is incomplete; mark the document at i + 1 as the next one + return i; + } + // If we made it to the end, we want to finish counting to see if we have a full document. + switch (parser.buf[parser.structural_indexes[0]]) { + case '}': + obj_cnt--; + break; + case ']': + arr_cnt--; + break; + case '{': + obj_cnt++; + break; + case '[': + arr_cnt++; + break; + } + if (!arr_cnt && !obj_cnt) { + // We have a complete document. + return parser.n_structural_indexes; + } + return 0; +} - } // namespace stage1 - } // unnamed namespace - } // namespace icelake +} // namespace stage1 +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_FIND_NEXT_DOCUMENT_INDEX_H @@ -24747,92 +26318,92 @@ namespace simdjson { // "simdjson/stage1.h" (this simplifies amalgation) namespace simdjson { - namespace icelake { - namespace { - namespace stage1 { +namespace icelake { +namespace { +namespace stage1 { - class json_minifier { - public: - template - static error_code minify(const uint8_t* buf, size_t len, uint8_t* dst, size_t& dst_len) noexcept; +class json_minifier { +public: + template + static error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) noexcept; - private: - simdjson_inline json_minifier(uint8_t* _dst) - : dst{ _dst } - {} - template - simdjson_inline void step(const uint8_t* block_buf, buf_block_reader& reader) noexcept; - simdjson_inline void next(const simd::simd8x64& in, const json_block& block); - simdjson_inline error_code finish(uint8_t* dst_start, size_t& dst_len); - json_scanner scanner{}; - uint8_t* dst; - }; +private: + simdjson_inline json_minifier(uint8_t *_dst) + : dst{_dst} + {} + template + simdjson_inline void step(const uint8_t *block_buf, buf_block_reader &reader) noexcept; + simdjson_inline void next(const simd::simd8x64& in, const json_block& block); + simdjson_warn_unused simdjson_inline error_code finish(uint8_t *dst_start, size_t &dst_len); + json_scanner scanner{}; + uint8_t *dst; +}; - simdjson_inline void json_minifier::next(const simd::simd8x64& in, const json_block& block) { - uint64_t mask = block.whitespace(); - dst += in.compress(mask, dst); - } +simdjson_inline void json_minifier::next(const simd::simd8x64& in, const json_block& block) { + uint64_t mask = block.whitespace(); + dst += in.compress(mask, dst); +} - simdjson_inline error_code json_minifier::finish(uint8_t* dst_start, size_t& dst_len) { - error_code error = scanner.finish(); - if (error) { dst_len = 0; return error; } - dst_len = dst - dst_start; - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code json_minifier::finish(uint8_t *dst_start, size_t &dst_len) { + error_code error = scanner.finish(); + if (error) { dst_len = 0; return error; } + dst_len = dst - dst_start; + return SUCCESS; +} - template<> - simdjson_inline void json_minifier::step<128>(const uint8_t* block_buf, buf_block_reader<128>& reader) noexcept { - simd::simd8x64 in_1(block_buf); - simd::simd8x64 in_2(block_buf + 64); - json_block block_1 = scanner.next(in_1); - json_block block_2 = scanner.next(in_2); - this->next(in_1, block_1); - this->next(in_2, block_2); - reader.advance(); - } +template<> +simdjson_inline void json_minifier::step<128>(const uint8_t *block_buf, buf_block_reader<128> &reader) noexcept { + simd::simd8x64 in_1(block_buf); + simd::simd8x64 in_2(block_buf+64); + json_block block_1 = scanner.next(in_1); + json_block block_2 = scanner.next(in_2); + this->next(in_1, block_1); + this->next(in_2, block_2); + reader.advance(); +} - template<> - simdjson_inline void json_minifier::step<64>(const uint8_t* block_buf, buf_block_reader<64>& reader) noexcept { - simd::simd8x64 in_1(block_buf); - json_block block_1 = scanner.next(in_1); - this->next(block_buf, block_1); - reader.advance(); - } +template<> +simdjson_inline void json_minifier::step<64>(const uint8_t *block_buf, buf_block_reader<64> &reader) noexcept { + simd::simd8x64 in_1(block_buf); + json_block block_1 = scanner.next(in_1); + this->next(block_buf, block_1); + reader.advance(); +} - template - error_code json_minifier::minify(const uint8_t* buf, size_t len, uint8_t* dst, size_t& dst_len) noexcept { - buf_block_reader reader(buf, len); - json_minifier minifier(dst); +template +error_code json_minifier::minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) noexcept { + buf_block_reader reader(buf, len); + json_minifier minifier(dst); - // Index the first n-1 blocks - while (reader.has_full_block()) { - minifier.step(reader.full_block(), reader); - } + // Index the first n-1 blocks + while (reader.has_full_block()) { + minifier.step(reader.full_block(), reader); + } - // Index the last (remainder) block, padded with spaces - uint8_t block[STEP_SIZE]; - size_t remaining_bytes = reader.get_remainder(block); - if (remaining_bytes > 0) { - // We do not want to write directly to the output stream. Rather, we write - // to a local buffer (for safety). - uint8_t out_block[STEP_SIZE]; - uint8_t* const guarded_dst{ minifier.dst }; - minifier.dst = out_block; - minifier.step(block, reader); - size_t to_write = minifier.dst - out_block; - // In some cases, we could be enticed to consider the padded spaces - // as part of the string. This is fine as long as we do not write more - // than we consumed. - if (to_write > remaining_bytes) { to_write = remaining_bytes; } - memcpy(guarded_dst, out_block, to_write); - minifier.dst = guarded_dst + to_write; - } - return minifier.finish(dst, dst_len); - } + // Index the last (remainder) block, padded with spaces + uint8_t block[STEP_SIZE]; + size_t remaining_bytes = reader.get_remainder(block); + if (remaining_bytes > 0) { + // We do not want to write directly to the output stream. Rather, we write + // to a local buffer (for safety). + uint8_t out_block[STEP_SIZE]; + uint8_t * const guarded_dst{minifier.dst}; + minifier.dst = out_block; + minifier.step(block, reader); + size_t to_write = minifier.dst - out_block; + // In some cases, we could be enticed to consider the padded spaces + // as part of the string. This is fine as long as we do not write more + // than we consumed. + if(to_write > remaining_bytes) { to_write = remaining_bytes; } + memcpy(guarded_dst, out_block, to_write); + minifier.dst = guarded_dst + to_write; + } + return minifier.finish(dst, dst_len); +} - } // namespace stage1 - } // unnamed namespace - } // namespace icelake +} // namespace stage1 +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_MINIFIER_H @@ -24858,341 +26429,339 @@ namespace simdjson { // "simdjson/stage1.h" (this simplifies amalgation) namespace simdjson { - namespace icelake { - namespace { - namespace stage1 { +namespace icelake { +namespace { +namespace stage1 { - class bit_indexer { - public: - uint32_t* tail; +class bit_indexer { +public: + uint32_t *tail; - simdjson_inline bit_indexer(uint32_t* index_buf) : tail(index_buf) {} + simdjson_inline bit_indexer(uint32_t *index_buf) : tail(index_buf) {} #if SIMDJSON_PREFER_REVERSE_BITS - /** - * ARM lacks a fast trailing zero instruction, but it has a fast - * bit reversal instruction and a fast leading zero instruction. - * Thus it may be profitable to reverse the bits (once) and then - * to rely on a sequence of instructions that call the leading - * zero instruction. - * - * Performance notes: - * The chosen routine is not optimal in terms of data dependency - * since zero_leading_bit might require two instructions. However, - * it tends to minimize the total number of instructions which is - * beneficial. - */ - simdjson_inline void write_index(uint32_t idx, uint64_t& rev_bits, int i) { - int lz = leading_zeroes(rev_bits); - this->tail[i] = static_cast(idx) + lz; - rev_bits = zero_leading_bit(rev_bits, lz); - } + /** + * ARM lacks a fast trailing zero instruction, but it has a fast + * bit reversal instruction and a fast leading zero instruction. + * Thus it may be profitable to reverse the bits (once) and then + * to rely on a sequence of instructions that call the leading + * zero instruction. + * + * Performance notes: + * The chosen routine is not optimal in terms of data dependency + * since zero_leading_bit might require two instructions. However, + * it tends to minimize the total number of instructions which is + * beneficial. + */ + simdjson_inline void write_index(uint32_t idx, uint64_t& rev_bits, int i) { + int lz = leading_zeroes(rev_bits); + this->tail[i] = static_cast(idx) + lz; + rev_bits = zero_leading_bit(rev_bits, lz); + } #else - /** - * Under recent x64 systems, we often have both a fast trailing zero - * instruction and a fast 'clear-lower-bit' instruction so the following - * algorithm can be competitive. - */ + /** + * Under recent x64 systems, we often have both a fast trailing zero + * instruction and a fast 'clear-lower-bit' instruction so the following + * algorithm can be competitive. + */ - simdjson_inline void write_index(uint32_t idx, uint64_t& bits, int i) { - this->tail[i] = idx + trailing_zeroes(bits); - bits = clear_lowest_bit(bits); - } + simdjson_inline void write_index(uint32_t idx, uint64_t& bits, int i) { + this->tail[i] = idx + trailing_zeroes(bits); + bits = clear_lowest_bit(bits); + } #endif // SIMDJSON_PREFER_REVERSE_BITS - template - simdjson_inline int write_indexes(uint32_t idx, uint64_t& bits) { - write_index(idx, bits, START); - SIMDJSON_IF_CONSTEXPR(N > 1) { - write_indexes<(N - 1 > 0 ? START + 1 : START), (N - 1 >= 0 ? N - 1 : 1)>(idx, bits); - } - return START + N; - } + template + simdjson_inline int write_indexes(uint32_t idx, uint64_t& bits) { + write_index(idx, bits, START); + SIMDJSON_IF_CONSTEXPR (N > 1) { + write_indexes<(N-1>0?START+1:START), (N-1>=0?N-1:1)>(idx, bits); + } + return START+N; + } - template - simdjson_inline int write_indexes_stepped(uint32_t idx, uint64_t& bits, int cnt) { - write_indexes(idx, bits); - SIMDJSON_IF_CONSTEXPR((START + STEP) < END) { - if (simdjson_unlikely((START + STEP) < cnt)) { - write_indexes_stepped<(START + STEP < END ? START + STEP : END), END, STEP>(idx, bits, cnt); - } - } - return ((END - START) % STEP) == 0 ? END : (END - START) - ((END - START) % STEP) + STEP; - } + template + simdjson_inline int write_indexes_stepped(uint32_t idx, uint64_t& bits, int cnt) { + write_indexes(idx, bits); + SIMDJSON_IF_CONSTEXPR ((START+STEP) < END) { + if (simdjson_unlikely((START+STEP) < cnt)) { + write_indexes_stepped<(START+STEP(idx, bits, cnt); + } + } + return ((END-START) % STEP) == 0 ? END : (END-START) - ((END-START) % STEP) + STEP; + } - // flatten out values in 'bits' assuming that they are are to have values of idx - // plus their position in the bitvector, and store these indexes at - // base_ptr[base] incrementing base as we go - // will potentially store extra values beyond end of valid bits, so base_ptr - // needs to be large enough to handle this - // - // If the kernel sets SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER, then it - // will provide its own version of the code. + // flatten out values in 'bits' assuming that they are are to have values of idx + // plus their position in the bitvector, and store these indexes at + // base_ptr[base] incrementing base as we go + // will potentially store extra values beyond end of valid bits, so base_ptr + // needs to be large enough to handle this + // + // If the kernel sets SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER, then it + // will provide its own version of the code. #ifdef SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER - simdjson_inline void write(uint32_t idx, uint64_t bits); + simdjson_inline void write(uint32_t idx, uint64_t bits); #else - simdjson_inline void write(uint32_t idx, uint64_t bits) { - // In some instances, the next branch is expensive because it is mispredicted. - // Unfortunately, in other cases, - // it helps tremendously. - if (bits == 0) - return; + simdjson_inline void write(uint32_t idx, uint64_t bits) { + // In some instances, the next branch is expensive because it is mispredicted. + // Unfortunately, in other cases, + // it helps tremendously. + if (bits == 0) + return; - int cnt = static_cast(count_ones(bits)); + int cnt = static_cast(count_ones(bits)); #if SIMDJSON_PREFER_REVERSE_BITS - bits = reverse_bits(bits); + bits = reverse_bits(bits); #endif #ifdef SIMDJSON_STRUCTURAL_INDEXER_STEP - static constexpr const int STEP = SIMDJSON_STRUCTURAL_INDEXER_STEP; + static constexpr const int STEP = SIMDJSON_STRUCTURAL_INDEXER_STEP; #else - static constexpr const int STEP = 4; + static constexpr const int STEP = 4; #endif - static constexpr const int STEP_UNTIL = 24; + static constexpr const int STEP_UNTIL = 24; - write_indexes_stepped<0, STEP_UNTIL, STEP>(idx, bits, cnt); - SIMDJSON_IF_CONSTEXPR(STEP_UNTIL < 64) { - if (simdjson_unlikely(STEP_UNTIL < cnt)) { - for (int i = STEP_UNTIL; i < cnt; i++) { - write_index(idx, bits, i); - } - } - } + write_indexes_stepped<0, STEP_UNTIL, STEP>(idx, bits, cnt); + SIMDJSON_IF_CONSTEXPR (STEP_UNTIL < 64) { + if (simdjson_unlikely(STEP_UNTIL < cnt)) { + for (int i=STEP_UNTIL; itail += cnt; - } + this->tail += cnt; + } #endif // SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER - }; +}; - class json_structural_indexer { - public: - /** - * Find the important bits of JSON in a 128-byte chunk, and add them to structural_indexes. - * - * @param partial Setting the partial parameter to true allows the find_structural_bits to - * tolerate unclosed strings. The caller should still ensure that the input is valid UTF-8. If - * you are processing substrings, you may want to call on a function like trimmed_length_safe_utf8. - */ - template - static error_code index(const uint8_t* buf, size_t len, dom_parser_implementation& parser, stage1_mode partial) noexcept; +class json_structural_indexer { +public: + /** + * Find the important bits of JSON in a 128-byte chunk, and add them to structural_indexes. + * + * @param partial Setting the partial parameter to true allows the find_structural_bits to + * tolerate unclosed strings. The caller should still ensure that the input is valid UTF-8. If + * you are processing substrings, you may want to call on a function like trimmed_length_safe_utf8. + */ + template + static error_code index(const uint8_t *buf, size_t len, dom_parser_implementation &parser, stage1_mode partial) noexcept; - private: - simdjson_inline json_structural_indexer(uint32_t* structural_indexes); - template - simdjson_inline void step(const uint8_t* block, buf_block_reader& reader) noexcept; - simdjson_inline void next(const simd::simd8x64& in, const json_block& block, size_t idx); - simdjson_inline error_code finish(dom_parser_implementation& parser, size_t idx, size_t len, stage1_mode partial); +private: + simdjson_inline json_structural_indexer(uint32_t *structural_indexes); + template + simdjson_inline void step(const uint8_t *block, buf_block_reader &reader) noexcept; + simdjson_inline void next(const simd::simd8x64& in, const json_block& block, size_t idx); + simdjson_warn_unused simdjson_inline error_code finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial); - json_scanner scanner{}; - utf8_checker checker{}; - bit_indexer indexer; - uint64_t prev_structurals = 0; - uint64_t unescaped_chars_error = 0; - }; + json_scanner scanner{}; + utf8_checker checker{}; + bit_indexer indexer; + uint64_t prev_structurals = 0; + uint64_t unescaped_chars_error = 0; +}; - simdjson_inline json_structural_indexer::json_structural_indexer(uint32_t* structural_indexes) : indexer{ structural_indexes } {} +simdjson_inline json_structural_indexer::json_structural_indexer(uint32_t *structural_indexes) : indexer{structural_indexes} {} - // Skip the last character if it is partial - simdjson_inline size_t trim_partial_utf8(const uint8_t* buf, size_t len) { - if (simdjson_unlikely(len < 3)) { - switch (len) { - case 2: - if (buf[len - 1] >= 0xc0) { return len - 1; } // 2-, 3- and 4-byte characters with only 1 byte left - if (buf[len - 2] >= 0xe0) { return len - 2; } // 3- and 4-byte characters with only 2 bytes left - return len; - case 1: - if (buf[len - 1] >= 0xc0) { return len - 1; } // 2-, 3- and 4-byte characters with only 1 byte left - return len; - case 0: - return len; - } - } - if (buf[len - 1] >= 0xc0) { return len - 1; } // 2-, 3- and 4-byte characters with only 1 byte left - if (buf[len - 2] >= 0xe0) { return len - 2; } // 3- and 4-byte characters with only 1 byte left - if (buf[len - 3] >= 0xf0) { return len - 3; } // 4-byte characters with only 3 bytes left - return len; - } +// Skip the last character if it is partial +simdjson_inline size_t trim_partial_utf8(const uint8_t *buf, size_t len) { + if (simdjson_unlikely(len < 3)) { + switch (len) { + case 2: + if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left + if (buf[len-2] >= 0xe0) { return len-2; } // 3- and 4-byte characters with only 2 bytes left + return len; + case 1: + if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left + return len; + case 0: + return len; + } + } + if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left + if (buf[len-2] >= 0xe0) { return len-2; } // 3- and 4-byte characters with only 1 byte left + if (buf[len-3] >= 0xf0) { return len-3; } // 4-byte characters with only 3 bytes left + return len; +} - // - // PERF NOTES: - // We pipe 2 inputs through these stages: - // 1. Load JSON into registers. This takes a long time and is highly parallelizable, so we load - // 2 inputs' worth at once so that by the time step 2 is looking for them input, it's available. - // 2. Scan the JSON for critical data: strings, scalars and operators. This is the critical path. - // The output of step 1 depends entirely on this information. These functions don't quite use - // up enough CPU: the second half of the functions is highly serial, only using 1 execution core - // at a time. The second input's scans has some dependency on the first ones finishing it, but - // they can make a lot of progress before they need that information. - // 3. Step 1 does not use enough capacity, so we run some extra stuff while we're waiting for that - // to finish: utf-8 checks and generating the output from the last iteration. - // - // The reason we run 2 inputs at a time, is steps 2 and 3 are *still* not enough to soak up all - // available capacity with just one input. Running 2 at a time seems to give the CPU a good enough - // workout. - // - template - error_code json_structural_indexer::index(const uint8_t* buf, size_t len, dom_parser_implementation& parser, stage1_mode partial) noexcept { - if (simdjson_unlikely(len > parser.capacity())) { return CAPACITY; } - // We guard the rest of the code so that we can assume that len > 0 throughout. - if (len == 0) { return EMPTY; } - if (is_streaming(partial)) { - len = trim_partial_utf8(buf, len); - // If you end up with an empty window after trimming - // the partial UTF-8 bytes, then chances are good that you - // have an UTF-8 formatting error. - if (len == 0) { return UTF8_ERROR; } - } - buf_block_reader reader(buf, len); - json_structural_indexer indexer(parser.structural_indexes.get()); +// +// PERF NOTES: +// We pipe 2 inputs through these stages: +// 1. Load JSON into registers. This takes a long time and is highly parallelizable, so we load +// 2 inputs' worth at once so that by the time step 2 is looking for them input, it's available. +// 2. Scan the JSON for critical data: strings, scalars and operators. This is the critical path. +// The output of step 1 depends entirely on this information. These functions don't quite use +// up enough CPU: the second half of the functions is highly serial, only using 1 execution core +// at a time. The second input's scans has some dependency on the first ones finishing it, but +// they can make a lot of progress before they need that information. +// 3. Step 1 does not use enough capacity, so we run some extra stuff while we're waiting for that +// to finish: utf-8 checks and generating the output from the last iteration. +// +// The reason we run 2 inputs at a time, is steps 2 and 3 are *still* not enough to soak up all +// available capacity with just one input. Running 2 at a time seems to give the CPU a good enough +// workout. +// +template +error_code json_structural_indexer::index(const uint8_t *buf, size_t len, dom_parser_implementation &parser, stage1_mode partial) noexcept { + if (simdjson_unlikely(len > parser.capacity())) { return CAPACITY; } + // We guard the rest of the code so that we can assume that len > 0 throughout. + if (len == 0) { return EMPTY; } + if (is_streaming(partial)) { + len = trim_partial_utf8(buf, len); + // If you end up with an empty window after trimming + // the partial UTF-8 bytes, then chances are good that you + // have an UTF-8 formatting error. + if(len == 0) { return UTF8_ERROR; } + } + buf_block_reader reader(buf, len); + json_structural_indexer indexer(parser.structural_indexes.get()); - // Read all but the last block - while (reader.has_full_block()) { - indexer.step(reader.full_block(), reader); - } - // Take care of the last block (will always be there unless file is empty which is - // not supposed to happen.) - uint8_t block[STEP_SIZE]; - if (simdjson_unlikely(reader.get_remainder(block) == 0)) { return UNEXPECTED_ERROR; } - indexer.step(block, reader); - return indexer.finish(parser, reader.block_index(), len, partial); - } + // Read all but the last block + while (reader.has_full_block()) { + indexer.step(reader.full_block(), reader); + } + // Take care of the last block (will always be there unless file is empty which is + // not supposed to happen.) + uint8_t block[STEP_SIZE]; + if (simdjson_unlikely(reader.get_remainder(block) == 0)) { return UNEXPECTED_ERROR; } + indexer.step(block, reader); + return indexer.finish(parser, reader.block_index(), len, partial); +} - template<> - simdjson_inline void json_structural_indexer::step<128>(const uint8_t* block, buf_block_reader<128>& reader) noexcept { - simd::simd8x64 in_1(block); - simd::simd8x64 in_2(block + 64); - json_block block_1 = scanner.next(in_1); - json_block block_2 = scanner.next(in_2); - this->next(in_1, block_1, reader.block_index()); - this->next(in_2, block_2, reader.block_index() + 64); - reader.advance(); - } +template<> +simdjson_inline void json_structural_indexer::step<128>(const uint8_t *block, buf_block_reader<128> &reader) noexcept { + simd::simd8x64 in_1(block); + simd::simd8x64 in_2(block+64); + json_block block_1 = scanner.next(in_1); + json_block block_2 = scanner.next(in_2); + this->next(in_1, block_1, reader.block_index()); + this->next(in_2, block_2, reader.block_index()+64); + reader.advance(); +} - template<> - simdjson_inline void json_structural_indexer::step<64>(const uint8_t* block, buf_block_reader<64>& reader) noexcept { - simd::simd8x64 in_1(block); - json_block block_1 = scanner.next(in_1); - this->next(in_1, block_1, reader.block_index()); - reader.advance(); - } +template<> +simdjson_inline void json_structural_indexer::step<64>(const uint8_t *block, buf_block_reader<64> &reader) noexcept { + simd::simd8x64 in_1(block); + json_block block_1 = scanner.next(in_1); + this->next(in_1, block_1, reader.block_index()); + reader.advance(); +} - simdjson_inline void json_structural_indexer::next(const simd::simd8x64& in, const json_block& block, size_t idx) { - uint64_t unescaped = in.lteq(0x1F); +simdjson_inline void json_structural_indexer::next(const simd::simd8x64& in, const json_block& block, size_t idx) { + uint64_t unescaped = in.lteq(0x1F); #if SIMDJSON_UTF8VALIDATION - checker.check_next_input(in); + checker.check_next_input(in); #endif - indexer.write(uint32_t(idx - 64), prev_structurals); // Output *last* iteration's structurals to the parser - prev_structurals = block.structural_start(); - unescaped_chars_error |= block.non_quote_inside_string(unescaped); - } + indexer.write(uint32_t(idx-64), prev_structurals); // Output *last* iteration's structurals to the parser + prev_structurals = block.structural_start(); + unescaped_chars_error |= block.non_quote_inside_string(unescaped); +} - simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementation& parser, size_t idx, size_t len, stage1_mode partial) { - // Write out the final iteration's structurals - indexer.write(uint32_t(idx - 64), prev_structurals); - error_code error = scanner.finish(); - // We deliberately break down the next expression so that it is - // human readable. - const bool should_we_exit = is_streaming(partial) ? - ((error != SUCCESS) && (error != UNCLOSED_STRING)) // when partial we tolerate UNCLOSED_STRING - : (error != SUCCESS); // if partial is false, we must have SUCCESS - const bool have_unclosed_string = (error == UNCLOSED_STRING); - if (simdjson_unlikely(should_we_exit)) { return error; } +simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial) { + // Write out the final iteration's structurals + indexer.write(uint32_t(idx-64), prev_structurals); + error_code error = scanner.finish(); + // We deliberately break down the next expression so that it is + // human readable. + const bool should_we_exit = is_streaming(partial) ? + ((error != SUCCESS) && (error != UNCLOSED_STRING)) // when partial we tolerate UNCLOSED_STRING + : (error != SUCCESS); // if partial is false, we must have SUCCESS + const bool have_unclosed_string = (error == UNCLOSED_STRING); + if (simdjson_unlikely(should_we_exit)) { return error; } - if (unescaped_chars_error) { - return UNESCAPED_CHARS; - } - parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get()); - /*** - * The On-Demand API requires special padding. - * - * This is related to https://github.com/simdjson/simdjson/issues/906 - * Basically, we want to make sure that if the parsing continues beyond the last (valid) - * structural character, it quickly stops. - * Only three structural characters can be repeated without triggering an error in JSON: [,] and }. - * We repeat the padding character (at 'len'). We don't know what it is, but if the parsing - * continues, then it must be [,] or }. - * Suppose it is ] or }. We backtrack to the first character, what could it be that would - * not trigger an error? It could be ] or } but no, because you can't start a document that way. - * It can't be a comma, a colon or any simple value. So the only way we could continue is - * if the repeated character is [. But if so, the document must start with [. But if the document - * starts with [, it should end with ]. If we enforce that rule, then we would get - * ][[ which is invalid. - * - * This is illustrated with the test array_iterate_unclosed_error() on the following input: - * R"({ "a": [,,)" - **/ - parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); // used later in partial == stage1_mode::streaming_final - parser.structural_indexes[parser.n_structural_indexes + 1] = uint32_t(len); - parser.structural_indexes[parser.n_structural_indexes + 2] = 0; - parser.next_structural_index = 0; - // a valid JSON file cannot have zero structural indexes - we should have found something - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { - return EMPTY; - } - if (simdjson_unlikely(parser.structural_indexes[parser.n_structural_indexes - 1] > len)) { - return UNEXPECTED_ERROR; - } - if (partial == stage1_mode::streaming_partial) { - // If we have an unclosed string, then the last structural - // will be the quote and we want to make sure to omit it. - if (have_unclosed_string) { - parser.n_structural_indexes--; - // a valid JSON file cannot have zero structural indexes - we should have found something - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; } - } - // We truncate the input to the end of the last complete document (or zero). - auto new_structural_indexes = find_next_document_index(parser); - if (new_structural_indexes == 0 && parser.n_structural_indexes > 0) { - if (parser.structural_indexes[0] == 0) { - // If the buffer is partial and we started at index 0 but the document is - // incomplete, it's too big to parse. - return CAPACITY; - } - else { - // It is possible that the document could be parsed, we just had a lot - // of white space. - parser.n_structural_indexes = 0; - return EMPTY; - } - } + if (unescaped_chars_error) { + return UNESCAPED_CHARS; + } + parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get()); + /*** + * The On-Demand API requires special padding. + * + * This is related to https://github.com/simdjson/simdjson/issues/906 + * Basically, we want to make sure that if the parsing continues beyond the last (valid) + * structural character, it quickly stops. + * Only three structural characters can be repeated without triggering an error in JSON: [,] and }. + * We repeat the padding character (at 'len'). We don't know what it is, but if the parsing + * continues, then it must be [,] or }. + * Suppose it is ] or }. We backtrack to the first character, what could it be that would + * not trigger an error? It could be ] or } but no, because you can't start a document that way. + * It can't be a comma, a colon or any simple value. So the only way we could continue is + * if the repeated character is [. But if so, the document must start with [. But if the document + * starts with [, it should end with ]. If we enforce that rule, then we would get + * ][[ which is invalid. + * + * This is illustrated with the test array_iterate_unclosed_error() on the following input: + * R"({ "a": [,,)" + **/ + parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); // used later in partial == stage1_mode::streaming_final + parser.structural_indexes[parser.n_structural_indexes + 1] = uint32_t(len); + parser.structural_indexes[parser.n_structural_indexes + 2] = 0; + parser.next_structural_index = 0; + // a valid JSON file cannot have zero structural indexes - we should have found something + if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { + return EMPTY; + } + if (simdjson_unlikely(parser.structural_indexes[parser.n_structural_indexes - 1] > len)) { + return UNEXPECTED_ERROR; + } + if (partial == stage1_mode::streaming_partial) { + // If we have an unclosed string, then the last structural + // will be the quote and we want to make sure to omit it. + if(have_unclosed_string) { + parser.n_structural_indexes--; + // a valid JSON file cannot have zero structural indexes - we should have found something + if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; } + } + // We truncate the input to the end of the last complete document (or zero). + auto new_structural_indexes = find_next_document_index(parser); + if (new_structural_indexes == 0 && parser.n_structural_indexes > 0) { + if(parser.structural_indexes[0] == 0) { + // If the buffer is partial and we started at index 0 but the document is + // incomplete, it's too big to parse. + return CAPACITY; + } else { + // It is possible that the document could be parsed, we just had a lot + // of white space. + parser.n_structural_indexes = 0; + return EMPTY; + } + } - parser.n_structural_indexes = new_structural_indexes; - } - else if (partial == stage1_mode::streaming_final) { - if (have_unclosed_string) { parser.n_structural_indexes--; } - // We truncate the input to the end of the last complete document (or zero). - // Because partial == stage1_mode::streaming_final, it means that we may - // silently ignore trailing garbage. Though it sounds bad, we do it - // deliberately because many people who have streams of JSON documents - // will truncate them for processing. E.g., imagine that you are uncompressing - // the data from a size file or receiving it in chunks from the network. You - // may not know where exactly the last document will be. Meanwhile the - // document_stream instances allow people to know the JSON documents they are - // parsing (see the iterator.source() method). - parser.n_structural_indexes = find_next_document_index(parser); - // We store the initial n_structural_indexes so that the client can see - // whether we used truncation. If initial_n_structural_indexes == parser.n_structural_indexes, - // then this will query parser.structural_indexes[parser.n_structural_indexes] which is len, - // otherwise, it will copy some prior index. - parser.structural_indexes[parser.n_structural_indexes + 1] = parser.structural_indexes[parser.n_structural_indexes]; - // This next line is critical, do not change it unless you understand what you are - // doing. - parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { - // We tolerate an unclosed string at the very end of the stream. Indeed, users - // often load their data in bulk without being careful and they want us to ignore - // the trailing garbage. - return EMPTY; - } - } - checker.check_eof(); - return checker.errors(); - } + parser.n_structural_indexes = new_structural_indexes; + } else if (partial == stage1_mode::streaming_final) { + if(have_unclosed_string) { parser.n_structural_indexes--; } + // We truncate the input to the end of the last complete document (or zero). + // Because partial == stage1_mode::streaming_final, it means that we may + // silently ignore trailing garbage. Though it sounds bad, we do it + // deliberately because many people who have streams of JSON documents + // will truncate them for processing. E.g., imagine that you are uncompressing + // the data from a size file or receiving it in chunks from the network. You + // may not know where exactly the last document will be. Meanwhile the + // document_stream instances allow people to know the JSON documents they are + // parsing (see the iterator.source() method). + parser.n_structural_indexes = find_next_document_index(parser); + // We store the initial n_structural_indexes so that the client can see + // whether we used truncation. If initial_n_structural_indexes == parser.n_structural_indexes, + // then this will query parser.structural_indexes[parser.n_structural_indexes] which is len, + // otherwise, it will copy some prior index. + parser.structural_indexes[parser.n_structural_indexes + 1] = parser.structural_indexes[parser.n_structural_indexes]; + // This next line is critical, do not change it unless you understand what you are + // doing. + parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); + if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { + // We tolerate an unclosed string at the very end of the stream. Indeed, users + // often load their data in bulk without being careful and they want us to ignore + // the trailing garbage. + return EMPTY; + } + } + checker.check_eof(); + return checker.errors(); +} - } // namespace stage1 - } // unnamed namespace - } // namespace icelake +} // namespace stage1 +} // unnamed namespace +} // namespace icelake } // namespace simdjson // Clear CUSTOM_BIT_INDEXER so other implementations can set it if they need to. @@ -25212,38 +26781,38 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { - namespace { - namespace stage1 { +namespace icelake { +namespace { +namespace stage1 { - /** - * Validates that the string is actual UTF-8. - */ - template - bool generic_validate_utf8(const uint8_t* input, size_t length) { - checker c{}; - buf_block_reader<64> reader(input, length); - while (reader.has_full_block()) { - simd::simd8x64 in(reader.full_block()); - c.check_next_input(in); - reader.advance(); - } - uint8_t block[64]{}; - reader.get_remainder(block); - simd::simd8x64 in(block); - c.check_next_input(in); - reader.advance(); - c.check_eof(); - return c.errors() == error_code::SUCCESS; - } +/** + * Validates that the string is actual UTF-8. + */ +template +bool generic_validate_utf8(const uint8_t * input, size_t length) { + checker c{}; + buf_block_reader<64> reader(input, length); + while (reader.has_full_block()) { + simd::simd8x64 in(reader.full_block()); + c.check_next_input(in); + reader.advance(); + } + uint8_t block[64]{}; + reader.get_remainder(block); + simd::simd8x64 in(block); + c.check_next_input(in); + reader.advance(); + c.check_eof(); + return c.errors() == error_code::SUCCESS; +} - bool generic_validate_utf8(const char* input, size_t length) { - return generic_validate_utf8(reinterpret_cast(input), length); - } +bool generic_validate_utf8(const char * input, size_t length) { + return generic_validate_utf8(reinterpret_cast(input),length); +} - } // namespace stage1 - } // unnamed namespace - } // namespace icelake +} // namespace stage1 +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_UTF8_VALIDATOR_H @@ -25262,18 +26831,18 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { - namespace { - namespace stage2 { +namespace icelake { +namespace { +namespace stage2 { - class json_iterator; - class structural_iterator; - struct tape_builder; - struct tape_writer; +class json_iterator; +class structural_iterator; +struct tape_builder; +struct tape_writer; - } // namespace stage2 - } // unnamed namespace - } // namespace icelake +} // namespace stage2 +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_BASE_H @@ -25291,109 +26860,109 @@ namespace simdjson { #include namespace simdjson { - namespace icelake { - namespace { - namespace stage2 { +namespace icelake { +namespace { +namespace stage2 { - struct tape_writer { - /** The next place to write to tape */ - uint64_t* next_tape_loc; +struct tape_writer { + /** The next place to write to tape */ + uint64_t *next_tape_loc; - /** Write a signed 64-bit value to tape. */ - simdjson_inline void append_s64(int64_t value) noexcept; + /** Write a signed 64-bit value to tape. */ + simdjson_inline void append_s64(int64_t value) noexcept; - /** Write an unsigned 64-bit value to tape. */ - simdjson_inline void append_u64(uint64_t value) noexcept; + /** Write an unsigned 64-bit value to tape. */ + simdjson_inline void append_u64(uint64_t value) noexcept; - /** Write a double value to tape. */ - simdjson_inline void append_double(double value) noexcept; + /** Write a double value to tape. */ + simdjson_inline void append_double(double value) noexcept; - /** - * Append a tape entry (an 8-bit type,and 56 bits worth of value). - */ - simdjson_inline void append(uint64_t val, internal::tape_type t) noexcept; + /** + * Append a tape entry (an 8-bit type,and 56 bits worth of value). + */ + simdjson_inline void append(uint64_t val, internal::tape_type t) noexcept; - /** - * Skip the current tape entry without writing. - * - * Used to skip the start of the container, since we'll come back later to fill it in when the - * container ends. - */ - simdjson_inline void skip() noexcept; + /** + * Skip the current tape entry without writing. + * + * Used to skip the start of the container, since we'll come back later to fill it in when the + * container ends. + */ + simdjson_inline void skip() noexcept; - /** - * Skip the number of tape entries necessary to write a large u64 or i64. - */ - simdjson_inline void skip_large_integer() noexcept; + /** + * Skip the number of tape entries necessary to write a large u64 or i64. + */ + simdjson_inline void skip_large_integer() noexcept; - /** - * Skip the number of tape entries necessary to write a double. - */ - simdjson_inline void skip_double() noexcept; + /** + * Skip the number of tape entries necessary to write a double. + */ + simdjson_inline void skip_double() noexcept; - /** - * Write a value to a known location on tape. - * - * Used to go back and write out the start of a container after the container ends. - */ - simdjson_inline static void write(uint64_t& tape_loc, uint64_t val, internal::tape_type t) noexcept; + /** + * Write a value to a known location on tape. + * + * Used to go back and write out the start of a container after the container ends. + */ + simdjson_inline static void write(uint64_t &tape_loc, uint64_t val, internal::tape_type t) noexcept; - private: - /** - * Append both the tape entry, and a supplementary value following it. Used for types that need - * all 64 bits, such as double and uint64_t. - */ - template - simdjson_inline void append2(uint64_t val, T val2, internal::tape_type t) noexcept; - }; // struct tape_writer +private: + /** + * Append both the tape entry, and a supplementary value following it. Used for types that need + * all 64 bits, such as double and uint64_t. + */ + template + simdjson_inline void append2(uint64_t val, T val2, internal::tape_type t) noexcept; +}; // struct tape_writer - simdjson_inline void tape_writer::append_s64(int64_t value) noexcept { - append2(0, value, internal::tape_type::INT64); - } +simdjson_inline void tape_writer::append_s64(int64_t value) noexcept { + append2(0, value, internal::tape_type::INT64); +} - simdjson_inline void tape_writer::append_u64(uint64_t value) noexcept { - append(0, internal::tape_type::UINT64); - *next_tape_loc = value; - next_tape_loc++; - } +simdjson_inline void tape_writer::append_u64(uint64_t value) noexcept { + append(0, internal::tape_type::UINT64); + *next_tape_loc = value; + next_tape_loc++; +} - /** Write a double value to tape. */ - simdjson_inline void tape_writer::append_double(double value) noexcept { - append2(0, value, internal::tape_type::DOUBLE); - } +/** Write a double value to tape. */ +simdjson_inline void tape_writer::append_double(double value) noexcept { + append2(0, value, internal::tape_type::DOUBLE); +} - simdjson_inline void tape_writer::skip() noexcept { - next_tape_loc++; - } +simdjson_inline void tape_writer::skip() noexcept { + next_tape_loc++; +} - simdjson_inline void tape_writer::skip_large_integer() noexcept { - next_tape_loc += 2; - } +simdjson_inline void tape_writer::skip_large_integer() noexcept { + next_tape_loc += 2; +} - simdjson_inline void tape_writer::skip_double() noexcept { - next_tape_loc += 2; - } +simdjson_inline void tape_writer::skip_double() noexcept { + next_tape_loc += 2; +} - simdjson_inline void tape_writer::append(uint64_t val, internal::tape_type t) noexcept { - *next_tape_loc = val | ((uint64_t(char(t))) << 56); - next_tape_loc++; - } +simdjson_inline void tape_writer::append(uint64_t val, internal::tape_type t) noexcept { + *next_tape_loc = val | ((uint64_t(char(t))) << 56); + next_tape_loc++; +} - template - simdjson_inline void tape_writer::append2(uint64_t val, T val2, internal::tape_type t) noexcept { - append(val, t); - static_assert(sizeof(val2) == sizeof(*next_tape_loc), "Type is not 64 bits!"); - memcpy(next_tape_loc, &val2, sizeof(val2)); - next_tape_loc++; - } +template +simdjson_inline void tape_writer::append2(uint64_t val, T val2, internal::tape_type t) noexcept { + append(val, t); + static_assert(sizeof(val2) == sizeof(*next_tape_loc), "Type is not 64 bits!"); + memcpy(next_tape_loc, &val2, sizeof(val2)); + next_tape_loc++; +} - simdjson_inline void tape_writer::write(uint64_t& tape_loc, uint64_t val, internal::tape_type t) noexcept { - tape_loc = val | ((uint64_t(char(t))) << 56); - } +simdjson_inline void tape_writer::write(uint64_t &tape_loc, uint64_t val, internal::tape_type t) noexcept { + tape_loc = val | ((uint64_t(char(t))) << 56); +} - } // namespace stage2 - } // unnamed namespace - } // namespace icelake +} // namespace stage2 +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_WRITER_H @@ -25413,92 +26982,90 @@ namespace simdjson { // This is for an internal-only stage 2 specific logger. // Set LOG_ENABLED = true to log what stage 2 is doing! namespace simdjson { - namespace icelake { - namespace { - namespace logger { +namespace icelake { +namespace { +namespace logger { - static constexpr const char* DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"; + static constexpr const char * DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"; #if SIMDJSON_VERBOSE_LOGGING - static constexpr const bool LOG_ENABLED = true; + static constexpr const bool LOG_ENABLED = true; #else - static constexpr const bool LOG_ENABLED = false; + static constexpr const bool LOG_ENABLED = false; #endif - 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 constexpr const int LOG_INDEX_LEN = 5; + 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 constexpr const int LOG_INDEX_LEN = 5; - static int log_depth; // Not threadsafe. Log only. + static int log_depth; // Not threadsafe. Log only. - // Helper to turn unprintable or newline characters into spaces - static simdjson_inline char printable_char(char c) { - if (c >= 0x20) { - return c; - } - else { - return ' '; - } - } + // Helper to turn unprintable or newline characters into spaces + static simdjson_inline char printable_char(char c) { + if (c >= 0x20) { + return c; + } else { + return ' '; + } + } - // Print the header and set up log_start - static simdjson_inline void log_start() { - if (LOG_ENABLED) { - log_depth = 0; - printf("\n"); - printf("| %-*s | %-*s | %-*s | %-*s | Detail |\n", LOG_EVENT_LEN, "Event", LOG_BUFFER_LEN, "Buffer", LOG_SMALL_BUFFER_LEN, "Next", 5, "Next#"); - printf("|%.*s|%.*s|%.*s|%.*s|--------|\n", LOG_EVENT_LEN + 2, DASHES, LOG_BUFFER_LEN + 2, DASHES, LOG_SMALL_BUFFER_LEN + 2, DASHES, 5 + 2, DASHES); - } - } + // Print the header and set up log_start + static simdjson_inline void log_start() { + if (LOG_ENABLED) { + log_depth = 0; + printf("\n"); + printf("| %-*s | %-*s | %-*s | %-*s | Detail |\n", LOG_EVENT_LEN, "Event", LOG_BUFFER_LEN, "Buffer", LOG_SMALL_BUFFER_LEN, "Next", 5, "Next#"); + printf("|%.*s|%.*s|%.*s|%.*s|--------|\n", LOG_EVENT_LEN+2, DASHES, LOG_BUFFER_LEN+2, DASHES, LOG_SMALL_BUFFER_LEN+2, DASHES, 5+2, DASHES); + } + } - simdjson_unused static simdjson_inline void log_string(const char* message) { - if (LOG_ENABLED) { - printf("%s\n", message); - } - } + simdjson_unused static simdjson_inline void log_string(const char *message) { + if (LOG_ENABLED) { + printf("%s\n", message); + } + } - // Logs a single line from the stage 2 DOM parser - template - static simdjson_inline void log_line(S& structurals, const char* title_prefix, const char* title, const char* detail) { - if (LOG_ENABLED) { - printf("| %*s%s%-*s ", log_depth * 2, "", title_prefix, LOG_EVENT_LEN - log_depth * 2 - int(strlen(title_prefix)), title); - auto current_index = structurals.at_beginning() ? nullptr : structurals.next_structural - 1; - auto next_index = structurals.next_structural; - auto current = current_index ? &structurals.buf[*current_index] : reinterpret_cast(" "); - auto next = &structurals.buf[*next_index]; - { - // Print the next N characters in the buffer. - printf("| "); - // Otherwise, print the characters starting from the buffer position. - // Print spaces for unprintable or newline characters. - for (int i = 0; i < LOG_BUFFER_LEN; i++) { - printf("%c", printable_char(current[i])); - } - printf(" "); - // Print the next N characters in the buffer. - printf("| "); - // Otherwise, print the characters starting from the buffer position. - // Print spaces for unprintable or newline characters. - for (int i = 0; i < LOG_SMALL_BUFFER_LEN; i++) { - printf("%c", printable_char(next[i])); - } - printf(" "); - } - if (current_index) { - printf("| %*u ", LOG_INDEX_LEN, *current_index); - } - else { - printf("| %-*s ", LOG_INDEX_LEN, ""); - } - // printf("| %*u ", LOG_INDEX_LEN, structurals.next_tape_index()); - printf("| %-s ", detail); - printf("|\n"); - } - } + // Logs a single line from the stage 2 DOM parser + template + static simdjson_inline void log_line(S &structurals, const char *title_prefix, const char *title, const char *detail) { + if (LOG_ENABLED) { + printf("| %*s%s%-*s ", log_depth*2, "", title_prefix, LOG_EVENT_LEN - log_depth*2 - int(strlen(title_prefix)), title); + auto current_index = structurals.at_beginning() ? nullptr : structurals.next_structural-1; + auto next_index = structurals.next_structural; + auto current = current_index ? &structurals.buf[*current_index] : reinterpret_cast(" "); + auto next = &structurals.buf[*next_index]; + { + // Print the next N characters in the buffer. + printf("| "); + // Otherwise, print the characters starting from the buffer position. + // Print spaces for unprintable or newline characters. + for (int i=0;i - simdjson_warn_unused simdjson_inline error_code walk_document(V& visitor) noexcept; + /** + * Walk the JSON document. + * + * The visitor receives callbacks when values are encountered. All callbacks pass the iterator as + * the first parameter; some callbacks have other parameters as well: + * + * - visit_document_start() - at the beginning. + * - visit_document_end() - at the end (if things were successful). + * + * - visit_array_start() - at the start `[` of a non-empty array. + * - visit_array_end() - at the end `]` of a non-empty array. + * - visit_empty_array() - when an empty array is encountered. + * + * - visit_object_end() - at the start `]` of a non-empty object. + * - visit_object_start() - at the end `]` of a non-empty object. + * - visit_empty_object() - when an empty object is encountered. + * - visit_key(const uint8_t *key) - when a key in an object field is encountered. key is + * guaranteed to point at the first quote of the string (`"key"`). + * - visit_primitive(const uint8_t *value) - when a value is a string, number, boolean or null. + * - visit_root_primitive(iter, uint8_t *value) - when the top-level value is a string, number, boolean or null. + * + * - increment_count(iter) - each time a value is found in an array or object. + */ + template + simdjson_warn_unused simdjson_inline error_code walk_document(V &visitor) noexcept; - /** - * Create an iterator capable of walking a JSON document. - * - * The document must have already passed through stage 1. - */ - simdjson_inline json_iterator(dom_parser_implementation& _dom_parser, size_t start_structural_index); + /** + * Create an iterator capable of walking a JSON document. + * + * The document must have already passed through stage 1. + */ + simdjson_inline json_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index); - /** - * Look at the next token. - * - * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). - * - * They may include invalid JSON as well (such as `1.2.3` or `ture`). - */ - simdjson_inline const uint8_t* peek() const noexcept; - /** - * Advance to the next token. - * - * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). - * - * They may include invalid JSON as well (such as `1.2.3` or `ture`). - */ - simdjson_inline const uint8_t* advance() noexcept; - /** - * Get the remaining length of the document, from the start of the current token. - */ - simdjson_inline size_t remaining_len() const noexcept; - /** - * Check if we are at the end of the document. - * - * If this is true, there are no more tokens. - */ - simdjson_inline bool at_eof() const noexcept; - /** - * Check if we are at the beginning of the document. - */ - simdjson_inline bool at_beginning() const noexcept; - simdjson_inline uint8_t last_structural() const noexcept; + /** + * Look at the next token. + * + * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). + * + * They may include invalid JSON as well (such as `1.2.3` or `ture`). + */ + simdjson_inline const uint8_t *peek() const noexcept; + /** + * Advance to the next token. + * + * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). + * + * They may include invalid JSON as well (such as `1.2.3` or `ture`). + */ + simdjson_inline const uint8_t *advance() noexcept; + /** + * Get the remaining length of the document, from the start of the current token. + */ + simdjson_inline size_t remaining_len() const noexcept; + /** + * Check if we are at the end of the document. + * + * If this is true, there are no more tokens. + */ + simdjson_inline bool at_eof() const noexcept; + /** + * Check if we are at the beginning of the document. + */ + simdjson_inline bool at_beginning() const noexcept; + simdjson_inline uint8_t last_structural() const noexcept; - /** - * Log that a value has been found. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_value(const char* type) const noexcept; - /** - * Log the start of a multipart value. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_start_value(const char* type) const noexcept; - /** - * Log the end of a multipart value. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_end_value(const char* type) const noexcept; - /** - * Log an error. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_error(const char* error) const noexcept; + /** + * Log that a value has been found. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_value(const char *type) const noexcept; + /** + * Log the start of a multipart value. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_start_value(const char *type) const noexcept; + /** + * Log the end of a multipart value. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_end_value(const char *type) const noexcept; + /** + * Log an error. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_error(const char *error) const noexcept; - template - simdjson_warn_unused simdjson_inline error_code visit_root_primitive(V& visitor, const uint8_t* value) noexcept; - template - simdjson_warn_unused simdjson_inline error_code visit_primitive(V& visitor, const uint8_t* value) noexcept; - }; + template + simdjson_warn_unused simdjson_inline error_code visit_root_primitive(V &visitor, const uint8_t *value) noexcept; + template + simdjson_warn_unused simdjson_inline error_code visit_primitive(V &visitor, const uint8_t *value) noexcept; +}; - template - simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V& visitor) noexcept { - logger::log_start(); +template +simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &visitor) noexcept { + logger::log_start(); - // - // Start the document - // - if (at_eof()) { return EMPTY; } - log_start_value("document"); - SIMDJSON_TRY(visitor.visit_document_start(*this)); + // + // Start the document + // + if (at_eof()) { return EMPTY; } + log_start_value("document"); + SIMDJSON_TRY( visitor.visit_document_start(*this) ); - // - // Read first value - // - { - auto value = advance(); + // + // Read first value + // + { + auto value = advance(); - // Make sure the outer object or array is closed before continuing; otherwise, there are ways we - // could get into memory corruption. See https://github.com/simdjson/simdjson/issues/906 - if (!STREAMING) { - switch (*value) { - case '{': if (last_structural() != '}') { log_value("starting brace unmatched"); return TAPE_ERROR; }; break; - case '[': if (last_structural() != ']') { log_value("starting bracket unmatched"); return TAPE_ERROR; }; break; - } - } + // Make sure the outer object or array is closed before continuing; otherwise, there are ways we + // could get into memory corruption. See https://github.com/simdjson/simdjson/issues/906 + if (!STREAMING) { + switch (*value) { + case '{': if (last_structural() != '}') { log_value("starting brace unmatched"); return TAPE_ERROR; }; break; + case '[': if (last_structural() != ']') { log_value("starting bracket unmatched"); return TAPE_ERROR; }; break; + } + } - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY(visitor.visit_empty_object(*this)); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY(visitor.visit_empty_array(*this)); break; } goto array_begin; - default: SIMDJSON_TRY(visitor.visit_root_primitive(*this, value)); break; - } - } - goto document_end; + switch (*value) { + case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; + case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; + default: SIMDJSON_TRY( visitor.visit_root_primitive(*this, value) ); break; + } + } + goto document_end; - // - // Object parser states - // - object_begin: - log_start_value("object"); - depth++; - if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } - dom_parser.is_array[depth] = false; - SIMDJSON_TRY(visitor.visit_object_start(*this)); +// +// Object parser states +// +object_begin: + log_start_value("object"); + depth++; + if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } + dom_parser.is_array[depth] = false; + SIMDJSON_TRY( visitor.visit_object_start(*this) ); - { - auto key = advance(); - if (*key != '"') { log_error("Object does not start with a key"); return TAPE_ERROR; } - SIMDJSON_TRY(visitor.increment_count(*this)); - SIMDJSON_TRY(visitor.visit_key(*this, key)); - } + { + auto key = advance(); + if (*key != '"') { log_error("Object does not start with a key"); return TAPE_ERROR; } + SIMDJSON_TRY( visitor.increment_count(*this) ); + SIMDJSON_TRY( visitor.visit_key(*this, key) ); + } - object_field: - if (simdjson_unlikely(*advance() != ':')) { log_error("Missing colon after key in object"); return TAPE_ERROR; } - { - auto value = advance(); - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY(visitor.visit_empty_object(*this)); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY(visitor.visit_empty_array(*this)); break; } goto array_begin; - default: SIMDJSON_TRY(visitor.visit_primitive(*this, value)); break; - } - } +object_field: + if (simdjson_unlikely( *advance() != ':' )) { log_error("Missing colon after key in object"); return TAPE_ERROR; } + { + auto value = advance(); + switch (*value) { + case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; + case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; + default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break; + } + } - object_continue: - switch (*advance()) { - case ',': - SIMDJSON_TRY(visitor.increment_count(*this)); - { - auto key = advance(); - if (simdjson_unlikely(*key != '"')) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; } - SIMDJSON_TRY(visitor.visit_key(*this, key)); - } - goto object_field; - case '}': log_end_value("object"); SIMDJSON_TRY(visitor.visit_object_end(*this)); goto scope_end; - default: log_error("No comma between object fields"); return TAPE_ERROR; - } +object_continue: + switch (*advance()) { + case ',': + SIMDJSON_TRY( visitor.increment_count(*this) ); + { + auto key = advance(); + if (simdjson_unlikely( *key != '"' )) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; } + SIMDJSON_TRY( visitor.visit_key(*this, key) ); + } + goto object_field; + case '}': log_end_value("object"); SIMDJSON_TRY( visitor.visit_object_end(*this) ); goto scope_end; + default: log_error("No comma between object fields"); return TAPE_ERROR; + } - scope_end: - depth--; - if (depth == 0) { goto document_end; } - if (dom_parser.is_array[depth]) { goto array_continue; } - goto object_continue; +scope_end: + depth--; + if (depth == 0) { goto document_end; } + if (dom_parser.is_array[depth]) { goto array_continue; } + goto object_continue; - // - // Array parser states - // - array_begin: - log_start_value("array"); - depth++; - if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } - dom_parser.is_array[depth] = true; - SIMDJSON_TRY(visitor.visit_array_start(*this)); - SIMDJSON_TRY(visitor.increment_count(*this)); +// +// Array parser states +// +array_begin: + log_start_value("array"); + depth++; + if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } + dom_parser.is_array[depth] = true; + SIMDJSON_TRY( visitor.visit_array_start(*this) ); + SIMDJSON_TRY( visitor.increment_count(*this) ); - array_value: - { - auto value = advance(); - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY(visitor.visit_empty_object(*this)); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY(visitor.visit_empty_array(*this)); break; } goto array_begin; - default: SIMDJSON_TRY(visitor.visit_primitive(*this, value)); break; - } - } +array_value: + { + auto value = advance(); + switch (*value) { + case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; + case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; + default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break; + } + } - array_continue: - switch (*advance()) { - case ',': SIMDJSON_TRY(visitor.increment_count(*this)); goto array_value; - case ']': log_end_value("array"); SIMDJSON_TRY(visitor.visit_array_end(*this)); goto scope_end; - default: log_error("Missing comma between array values"); return TAPE_ERROR; - } +array_continue: + switch (*advance()) { + case ',': SIMDJSON_TRY( visitor.increment_count(*this) ); goto array_value; + case ']': log_end_value("array"); SIMDJSON_TRY( visitor.visit_array_end(*this) ); goto scope_end; + default: log_error("Missing comma between array values"); return TAPE_ERROR; + } - document_end: - log_end_value("document"); - SIMDJSON_TRY(visitor.visit_document_end(*this)); +document_end: + log_end_value("document"); + SIMDJSON_TRY( visitor.visit_document_end(*this) ); - dom_parser.next_structural_index = uint32_t(next_structural - &dom_parser.structural_indexes[0]); + dom_parser.next_structural_index = uint32_t(next_structural - &dom_parser.structural_indexes[0]); - // If we didn't make it to the end, it's an error - if (!STREAMING && dom_parser.next_structural_index != dom_parser.n_structural_indexes) { - log_error("More than one JSON value at the root of the document, or extra characters at the end of the JSON!"); - return TAPE_ERROR; - } + // If we didn't make it to the end, it's an error + if ( !STREAMING && dom_parser.next_structural_index != dom_parser.n_structural_indexes ) { + log_error("More than one JSON value at the root of the document, or extra characters at the end of the JSON!"); + return TAPE_ERROR; + } - return SUCCESS; + return SUCCESS; - } // walk_document() +} // walk_document() - simdjson_inline json_iterator::json_iterator(dom_parser_implementation& _dom_parser, size_t start_structural_index) - : buf{ _dom_parser.buf }, - next_structural{ &_dom_parser.structural_indexes[start_structural_index] }, - dom_parser{ _dom_parser } { - } +simdjson_inline json_iterator::json_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index) + : buf{_dom_parser.buf}, + next_structural{&_dom_parser.structural_indexes[start_structural_index]}, + dom_parser{_dom_parser} { +} - simdjson_inline const uint8_t* json_iterator::peek() const noexcept { - return &buf[*(next_structural)]; - } - simdjson_inline const uint8_t* json_iterator::advance() noexcept { - return &buf[*(next_structural++)]; - } - simdjson_inline size_t json_iterator::remaining_len() const noexcept { - return dom_parser.len - *(next_structural - 1); - } +simdjson_inline const uint8_t *json_iterator::peek() const noexcept { + return &buf[*(next_structural)]; +} +simdjson_inline const uint8_t *json_iterator::advance() noexcept { + return &buf[*(next_structural++)]; +} +simdjson_inline size_t json_iterator::remaining_len() const noexcept { + return dom_parser.len - *(next_structural-1); +} - simdjson_inline bool json_iterator::at_eof() const noexcept { - return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; - } - simdjson_inline bool json_iterator::at_beginning() const noexcept { - return next_structural == dom_parser.structural_indexes.get(); - } - simdjson_inline uint8_t json_iterator::last_structural() const noexcept { - return buf[dom_parser.structural_indexes[dom_parser.n_structural_indexes - 1]]; - } +simdjson_inline bool json_iterator::at_eof() const noexcept { + return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; +} +simdjson_inline bool json_iterator::at_beginning() const noexcept { + return next_structural == dom_parser.structural_indexes.get(); +} +simdjson_inline uint8_t json_iterator::last_structural() const noexcept { + return buf[dom_parser.structural_indexes[dom_parser.n_structural_indexes - 1]]; +} - simdjson_inline void json_iterator::log_value(const char* type) const noexcept { - logger::log_line(*this, "", type, ""); - } +simdjson_inline void json_iterator::log_value(const char *type) const noexcept { + logger::log_line(*this, "", type, ""); +} - simdjson_inline void json_iterator::log_start_value(const char* type) const noexcept { - logger::log_line(*this, "+", type, ""); - if (logger::LOG_ENABLED) { logger::log_depth++; } - } +simdjson_inline void json_iterator::log_start_value(const char *type) const noexcept { + logger::log_line(*this, "+", type, ""); + if (logger::LOG_ENABLED) { logger::log_depth++; } +} - simdjson_inline void json_iterator::log_end_value(const char* type) const noexcept { - if (logger::LOG_ENABLED) { logger::log_depth--; } - logger::log_line(*this, "-", type, ""); - } +simdjson_inline void json_iterator::log_end_value(const char *type) const noexcept { + if (logger::LOG_ENABLED) { logger::log_depth--; } + logger::log_line(*this, "-", type, ""); +} - simdjson_inline void json_iterator::log_error(const char* error) const noexcept { - logger::log_line(*this, "", "ERROR", error); - } +simdjson_inline void json_iterator::log_error(const char *error) const noexcept { + logger::log_line(*this, "", "ERROR", error); +} - template - simdjson_warn_unused simdjson_inline error_code json_iterator::visit_root_primitive(V& visitor, const uint8_t* value) noexcept { - switch (*value) { - case '"': return visitor.visit_root_string(*this, value); - case 't': return visitor.visit_root_true_atom(*this, value); - case 'f': return visitor.visit_root_false_atom(*this, value); - case 'n': return visitor.visit_root_null_atom(*this, value); - case '-': - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - return visitor.visit_root_number(*this, value); - default: - log_error("Document starts with a non-value character"); - return TAPE_ERROR; - } - } - template - simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V& visitor, const uint8_t* value) noexcept { - // Use the fact that most scalars are going to be either strings or numbers. - if (*value == '"') { - return visitor.visit_string(*this, value); - } - else if (((*value - '0') < 10) || (*value == '-')) { - return visitor.visit_number(*this, value); - } - // true, false, null are uncommon. - switch (*value) { - case 't': return visitor.visit_true_atom(*this, value); - case 'f': return visitor.visit_false_atom(*this, value); - case 'n': return visitor.visit_null_atom(*this, value); - default: - log_error("Non-value found when value was expected!"); - return TAPE_ERROR; - } - } +template +simdjson_warn_unused simdjson_inline error_code json_iterator::visit_root_primitive(V &visitor, const uint8_t *value) noexcept { + switch (*value) { + case '"': return visitor.visit_root_string(*this, value); + case 't': return visitor.visit_root_true_atom(*this, value); + case 'f': return visitor.visit_root_false_atom(*this, value); + case 'n': return visitor.visit_root_null_atom(*this, value); + case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return visitor.visit_root_number(*this, value); + default: + log_error("Document starts with a non-value character"); + return TAPE_ERROR; + } +} +template +simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V &visitor, const uint8_t *value) noexcept { + // Use the fact that most scalars are going to be either strings or numbers. + if(*value == '"') { + return visitor.visit_string(*this, value); + } else if (((*value - '0') < 10) || (*value == '-')) { + return visitor.visit_number(*this, value); + } + // true, false, null are uncommon. + switch (*value) { + case 't': return visitor.visit_true_atom(*this, value); + case 'f': return visitor.visit_false_atom(*this, value); + case 'n': return visitor.visit_null_atom(*this, value); + default: + log_error("Non-value found when value was expected!"); + return TAPE_ERROR; + } +} - } // namespace stage2 - } // unnamed namespace - } // namespace icelake +} // namespace stage2 +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_JSON_ITERATOR_H /* end file generic/stage2/json_iterator.h for icelake */ /* including generic/stage2/stringparsing.h for icelake: #include */ /* begin file generic/stage2/stringparsing.h for icelake */ +#include #ifndef SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -25851,242 +27418,238 @@ namespace simdjson { // It is intended to be included multiple times and compiled multiple times namespace simdjson { - namespace icelake { - namespace { - /// @private - namespace stringparsing { +namespace icelake { +namespace { +/// @private +namespace stringparsing { - // begin copypasta - // These chars yield themselves: " \ / - // b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab - // u not handled in this table as it's complex - static const uint8_t escape_map[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// begin copypasta +// These chars yield themselves: " \ / +// b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab +// u not handled in this table as it's complex +static const uint8_t escape_map[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0. + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5. - 0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6. - 0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7. + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4. + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5. + 0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6. + 0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - }; + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; - // handle a unicode codepoint - // write appropriate values into dest - // src will advance 6 bytes or 12 bytes - // dest will advance a variable amount (return via pointer) - // return true if the unicode codepoint was valid - // We work in little-endian then swap at write time - simdjson_warn_unused - simdjson_inline bool handle_unicode_codepoint(const uint8_t** src_ptr, - uint8_t** dst_ptr, bool allow_replacement) { - // Use the default Unicode Character 'REPLACEMENT CHARACTER' (U+FFFD) - constexpr uint32_t substitution_code_point = 0xfffd; - // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the - // conversion is not valid; we defer the check for this to inside the - // multilingual plane check. - uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); - *src_ptr += 6; +// handle a unicode codepoint +// write appropriate values into dest +// src will advance 6 bytes or 12 bytes +// dest will advance a variable amount (return via pointer) +// return true if the unicode codepoint was valid +// We work in little-endian then swap at write time +simdjson_warn_unused +simdjson_inline bool handle_unicode_codepoint(const uint8_t **src_ptr, + uint8_t **dst_ptr, bool allow_replacement) { + // Use the default Unicode Character 'REPLACEMENT CHARACTER' (U+FFFD) + constexpr uint32_t substitution_code_point = 0xfffd; + // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the + // conversion is not valid; we defer the check for this to inside the + // multilingual plane check. + uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); + *src_ptr += 6; - // If we found a high surrogate, we must - // check for low surrogate for characters - // outside the Basic - // Multilingual Plane. - if (code_point >= 0xd800 && code_point < 0xdc00) { - const uint8_t* src_data = *src_ptr; - /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ - if (((src_data[0] << 8) | src_data[1]) != ((static_cast ('\\') << 8) | static_cast ('u'))) { - if (!allow_replacement) { return false; } - code_point = substitution_code_point; - } - else { - uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); + // If we found a high surrogate, we must + // check for low surrogate for characters + // outside the Basic + // Multilingual Plane. + if (code_point >= 0xd800 && code_point < 0xdc00) { + const uint8_t *src_data = *src_ptr; + /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ + if (((src_data[0] << 8) | src_data[1]) != ((static_cast ('\\') << 8) | static_cast ('u'))) { + if(!allow_replacement) { return false; } + code_point = substitution_code_point; + } else { + uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); - // We have already checked that the high surrogate is valid and - // (code_point - 0xd800) < 1024. - // - // Check that code_point_2 is in the range 0xdc00..0xdfff - // and that code_point_2 was parsed from valid hex. - uint32_t low_bit = code_point_2 - 0xdc00; - if (low_bit >> 10) { - if (!allow_replacement) { return false; } - code_point = substitution_code_point; - } - else { - code_point = (((code_point - 0xd800) << 10) | low_bit) + 0x10000; - *src_ptr += 6; - } + // We have already checked that the high surrogate is valid and + // (code_point - 0xd800) < 1024. + // + // Check that code_point_2 is in the range 0xdc00..0xdfff + // and that code_point_2 was parsed from valid hex. + uint32_t low_bit = code_point_2 - 0xdc00; + if (low_bit >> 10) { + if(!allow_replacement) { return false; } + code_point = substitution_code_point; + } else { + code_point = (((code_point - 0xd800) << 10) | low_bit) + 0x10000; + *src_ptr += 6; + } - } - } - else if (code_point >= 0xdc00 && code_point <= 0xdfff) { - // If we encounter a low surrogate (not preceded by a high surrogate) - // then we have an error. - if (!allow_replacement) { return false; } - code_point = substitution_code_point; - } - size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); - *dst_ptr += offset; - return offset > 0; - } + } + } else if (code_point >= 0xdc00 && code_point <= 0xdfff) { + // If we encounter a low surrogate (not preceded by a high surrogate) + // then we have an error. + if(!allow_replacement) { return false; } + code_point = substitution_code_point; + } + size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); + *dst_ptr += offset; + return offset > 0; +} - // handle a unicode codepoint using the wobbly convention - // https://simonsapin.github.io/wtf-8/ - // write appropriate values into dest - // src will advance 6 bytes or 12 bytes - // dest will advance a variable amount (return via pointer) - // return true if the unicode codepoint was valid - // We work in little-endian then swap at write time - simdjson_warn_unused - simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t** src_ptr, - uint8_t** dst_ptr) { - // It is not ideal that this function is nearly identical to handle_unicode_codepoint. - // - // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the - // conversion is not valid; we defer the check for this to inside the - // multilingual plane check. - uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); - *src_ptr += 6; - // If we found a high surrogate, we must - // check for low surrogate for characters - // outside the Basic - // Multilingual Plane. - if (code_point >= 0xd800 && code_point < 0xdc00) { - const uint8_t* src_data = *src_ptr; - /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ - if (((src_data[0] << 8) | src_data[1]) == ((static_cast ('\\') << 8) | static_cast ('u'))) { - uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); - uint32_t low_bit = code_point_2 - 0xdc00; - if ((low_bit >> 10) == 0) { - code_point = - (((code_point - 0xd800) << 10) | low_bit) + 0x10000; - *src_ptr += 6; - } - } - } +// handle a unicode codepoint using the wobbly convention +// https://simonsapin.github.io/wtf-8/ +// write appropriate values into dest +// src will advance 6 bytes or 12 bytes +// dest will advance a variable amount (return via pointer) +// return true if the unicode codepoint was valid +// We work in little-endian then swap at write time +simdjson_warn_unused +simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t **src_ptr, + uint8_t **dst_ptr) { + // It is not ideal that this function is nearly identical to handle_unicode_codepoint. + // + // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the + // conversion is not valid; we defer the check for this to inside the + // multilingual plane check. + uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); + *src_ptr += 6; + // If we found a high surrogate, we must + // check for low surrogate for characters + // outside the Basic + // Multilingual Plane. + if (code_point >= 0xd800 && code_point < 0xdc00) { + const uint8_t *src_data = *src_ptr; + /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ + if (((src_data[0] << 8) | src_data[1]) == ((static_cast ('\\') << 8) | static_cast ('u'))) { + uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); + uint32_t low_bit = code_point_2 - 0xdc00; + if ((low_bit >> 10) == 0) { + code_point = + (((code_point - 0xd800) << 10) | low_bit) + 0x10000; + *src_ptr += 6; + } + } + } - size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); - *dst_ptr += offset; - return offset > 0; - } + size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); + *dst_ptr += offset; + return offset > 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. - */ - simdjson_warn_unused simdjson_inline uint8_t* parse_string(const uint8_t* src, uint8_t* dst, bool allow_replacement) { - while (1) { - // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); - // If the next thing is the end quote, copy and return - if (bs_quote.has_quote_first()) { - // we encountered quotes first. Move dst to point to quotes and exit - return dst + bs_quote.quote_index(); - } - if (bs_quote.has_backslash()) { - /* find out where the backspace is */ - auto bs_dist = bs_quote.backslash_index(); - uint8_t escape_char = src[bs_dist + 1]; - /* we encountered backslash first. Handle backslash */ - if (escape_char == 'u') { - /* move src/dst up to the start; they will be further adjusted - within the unicode codepoint handling code. */ - src += bs_dist; - dst += bs_dist; - if (!handle_unicode_codepoint(&src, &dst, allow_replacement)) { - return nullptr; - } - } - else { - /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and - * write bs_dist+1 characters to output - * note this may reach beyond the part of the buffer we've actually - * seen. I think this is ok */ - uint8_t escape_result = escape_map[escape_char]; - if (escape_result == 0u) { - return nullptr; /* bogus escape value is an error */ - } - dst[bs_dist] = escape_result; - src += bs_dist + 2; - dst += bs_dist + 1; - } - } - else { - /* they are the same. Since they can't co-occur, it means we - * encountered neither. */ - src += backslash_and_quote::BYTES_PROCESSED; - dst += backslash_and_quote::BYTES_PROCESSED; - } - } - } +/** + * 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. + */ +simdjson_warn_unused simdjson_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) { + while (1) { + // Copy the next n bytes, and find the backslash and quote in them. + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); + // If the next thing is the end quote, copy and return + if (bs_quote.has_quote_first()) { + // we encountered quotes first. Move dst to point to quotes and exit + return dst + bs_quote.quote_index(); + } + if (bs_quote.has_backslash()) { + /* find out where the backspace is */ + auto bs_dist = bs_quote.backslash_index(); + uint8_t escape_char = src[bs_dist + 1]; + /* we encountered backslash first. Handle backslash */ + if (escape_char == 'u') { + /* move src/dst up to the start; they will be further adjusted + within the unicode codepoint handling code. */ + src += bs_dist; + dst += bs_dist; + if (!handle_unicode_codepoint(&src, &dst, allow_replacement)) { + return nullptr; + } + } else { + /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and + * write bs_dist+1 characters to output + * note this may reach beyond the part of the buffer we've actually + * seen. I think this is ok */ + uint8_t escape_result = escape_map[escape_char]; + if (escape_result == 0u) { + return nullptr; /* bogus escape value is an error */ + } + dst[bs_dist] = escape_result; + src += bs_dist + 2; + dst += bs_dist + 1; + } + } else { + /* they are the same. Since they can't co-occur, it means we + * encountered neither. */ + src += backslash_and_quote::BYTES_PROCESSED; + dst += backslash_and_quote::BYTES_PROCESSED; + } + } +} - simdjson_warn_unused simdjson_inline uint8_t* parse_wobbly_string(const uint8_t* src, uint8_t* dst) { - // It is not ideal that this function is nearly identical to parse_string. - while (1) { - // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); - // If the next thing is the end quote, copy and return - if (bs_quote.has_quote_first()) { - // we encountered quotes first. Move dst to point to quotes and exit - return dst + bs_quote.quote_index(); - } - if (bs_quote.has_backslash()) { - /* find out where the backspace is */ - auto bs_dist = bs_quote.backslash_index(); - uint8_t escape_char = src[bs_dist + 1]; - /* we encountered backslash first. Handle backslash */ - if (escape_char == 'u') { - /* move src/dst up to the start; they will be further adjusted - within the unicode codepoint handling code. */ - src += bs_dist; - dst += bs_dist; - if (!handle_unicode_codepoint_wobbly(&src, &dst)) { - return nullptr; - } - } - else { - /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and - * write bs_dist+1 characters to output - * note this may reach beyond the part of the buffer we've actually - * seen. I think this is ok */ - uint8_t escape_result = escape_map[escape_char]; - if (escape_result == 0u) { - return nullptr; /* bogus escape value is an error */ - } - dst[bs_dist] = escape_result; - src += bs_dist + 2; - dst += bs_dist + 1; - } - } - else { - /* they are the same. Since they can't co-occur, it means we - * encountered neither. */ - src += backslash_and_quote::BYTES_PROCESSED; - dst += backslash_and_quote::BYTES_PROCESSED; - } - } - } +simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) { + // It is not ideal that this function is nearly identical to parse_string. + while (1) { + // Copy the next n bytes, and find the backslash and quote in them. + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); + // If the next thing is the end quote, copy and return + if (bs_quote.has_quote_first()) { + // we encountered quotes first. Move dst to point to quotes and exit + return dst + bs_quote.quote_index(); + } + if (bs_quote.has_backslash()) { + /* find out where the backspace is */ + auto bs_dist = bs_quote.backslash_index(); + uint8_t escape_char = src[bs_dist + 1]; + /* we encountered backslash first. Handle backslash */ + if (escape_char == 'u') { + /* move src/dst up to the start; they will be further adjusted + within the unicode codepoint handling code. */ + src += bs_dist; + dst += bs_dist; + if (!handle_unicode_codepoint_wobbly(&src, &dst)) { + return nullptr; + } + } else { + /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and + * write bs_dist+1 characters to output + * note this may reach beyond the part of the buffer we've actually + * seen. I think this is ok */ + uint8_t escape_result = escape_map[escape_char]; + if (escape_result == 0u) { + return nullptr; /* bogus escape value is an error */ + } + dst[bs_dist] = escape_result; + src += bs_dist + 2; + dst += bs_dist + 1; + } + } else { + /* they are the same. Since they can't co-occur, it means we + * encountered neither. */ + src += backslash_and_quote::BYTES_PROCESSED; + dst += backslash_and_quote::BYTES_PROCESSED; + } + } +} - } // namespace stringparsing - } // unnamed namespace - } // namespace icelake +} // namespace stringparsing + +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H @@ -26102,58 +27665,58 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { - namespace { - namespace stage2 { +namespace icelake { +namespace { +namespace stage2 { - class structural_iterator { - public: - const uint8_t* const buf; - uint32_t* next_structural; - dom_parser_implementation& dom_parser; +class structural_iterator { +public: + const uint8_t* const buf; + uint32_t *next_structural; + dom_parser_implementation &dom_parser; - // Start a structural - simdjson_inline structural_iterator(dom_parser_implementation& _dom_parser, size_t start_structural_index) - : buf{ _dom_parser.buf }, - next_structural{ &_dom_parser.structural_indexes[start_structural_index] }, - dom_parser{ _dom_parser } { - } - // Get the buffer position of the current structural character - simdjson_inline const uint8_t* current() { - return &buf[*(next_structural - 1)]; - } - // Get the current structural character - simdjson_inline char current_char() { - return buf[*(next_structural - 1)]; - } - // Get the next structural character without advancing - simdjson_inline char peek_next_char() { - return buf[*next_structural]; - } - simdjson_inline const uint8_t* peek() { - return &buf[*next_structural]; - } - simdjson_inline const uint8_t* advance() { - return &buf[*(next_structural++)]; - } - simdjson_inline char advance_char() { - return buf[*(next_structural++)]; - } - simdjson_inline size_t remaining_len() { - return dom_parser.len - *(next_structural - 1); - } + // Start a structural + simdjson_inline structural_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index) + : buf{_dom_parser.buf}, + next_structural{&_dom_parser.structural_indexes[start_structural_index]}, + dom_parser{_dom_parser} { + } + // Get the buffer position of the current structural character + simdjson_inline const uint8_t* current() { + return &buf[*(next_structural-1)]; + } + // Get the current structural character + simdjson_inline char current_char() { + return buf[*(next_structural-1)]; + } + // Get the next structural character without advancing + simdjson_inline char peek_next_char() { + return buf[*next_structural]; + } + simdjson_inline const uint8_t* peek() { + return &buf[*next_structural]; + } + simdjson_inline const uint8_t* advance() { + return &buf[*(next_structural++)]; + } + simdjson_inline char advance_char() { + return buf[*(next_structural++)]; + } + simdjson_inline size_t remaining_len() { + return dom_parser.len - *(next_structural-1); + } - simdjson_inline bool at_end() { - return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; - } - simdjson_inline bool at_beginning() { - return next_structural == dom_parser.structural_indexes.get(); - } - }; + simdjson_inline bool at_end() { + return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; + } + simdjson_inline bool at_beginning() { + return next_structural == dom_parser.structural_indexes.get(); + } +}; - } // namespace stage2 - } // unnamed namespace - } // namespace icelake +} // namespace stage2 +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_STRUCTURAL_ITERATOR_H @@ -26176,284 +27739,284 @@ namespace simdjson { namespace simdjson { - namespace icelake { - namespace { - namespace stage2 { +namespace icelake { +namespace { +namespace stage2 { - struct tape_builder { - template - simdjson_warn_unused static simdjson_inline error_code parse_document( - dom_parser_implementation& dom_parser, - dom::document& doc) noexcept; +struct tape_builder { + template + simdjson_warn_unused static simdjson_inline error_code parse_document( + dom_parser_implementation &dom_parser, + dom::document &doc) noexcept; - /** Called when a non-empty document starts. */ - simdjson_warn_unused simdjson_inline error_code visit_document_start(json_iterator& iter) noexcept; - /** Called when a non-empty document ends without error. */ - simdjson_warn_unused simdjson_inline error_code visit_document_end(json_iterator& iter) noexcept; + /** Called when a non-empty document starts. */ + simdjson_warn_unused simdjson_inline error_code visit_document_start(json_iterator &iter) noexcept; + /** Called when a non-empty document ends without error. */ + simdjson_warn_unused simdjson_inline error_code visit_document_end(json_iterator &iter) noexcept; - /** Called when a non-empty array starts. */ - simdjson_warn_unused simdjson_inline error_code visit_array_start(json_iterator& iter) noexcept; - /** Called when a non-empty array ends. */ - simdjson_warn_unused simdjson_inline error_code visit_array_end(json_iterator& iter) noexcept; - /** Called when an empty array is found. */ - simdjson_warn_unused simdjson_inline error_code visit_empty_array(json_iterator& iter) noexcept; + /** Called when a non-empty array starts. */ + simdjson_warn_unused simdjson_inline error_code visit_array_start(json_iterator &iter) noexcept; + /** Called when a non-empty array ends. */ + simdjson_warn_unused simdjson_inline error_code visit_array_end(json_iterator &iter) noexcept; + /** Called when an empty array is found. */ + simdjson_warn_unused simdjson_inline error_code visit_empty_array(json_iterator &iter) noexcept; - /** Called when a non-empty object starts. */ - simdjson_warn_unused simdjson_inline error_code visit_object_start(json_iterator& iter) noexcept; - /** - * Called when a key in a field is encountered. - * - * primitive, visit_object_start, visit_empty_object, visit_array_start, or visit_empty_array - * will be called after this with the field value. - */ - simdjson_warn_unused simdjson_inline error_code visit_key(json_iterator& iter, const uint8_t* key) noexcept; - /** Called when a non-empty object ends. */ - simdjson_warn_unused simdjson_inline error_code visit_object_end(json_iterator& iter) noexcept; - /** Called when an empty object is found. */ - simdjson_warn_unused simdjson_inline error_code visit_empty_object(json_iterator& iter) noexcept; + /** Called when a non-empty object starts. */ + simdjson_warn_unused simdjson_inline error_code visit_object_start(json_iterator &iter) noexcept; + /** + * Called when a key in a field is encountered. + * + * primitive, visit_object_start, visit_empty_object, visit_array_start, or visit_empty_array + * will be called after this with the field value. + */ + simdjson_warn_unused simdjson_inline error_code visit_key(json_iterator &iter, const uint8_t *key) noexcept; + /** Called when a non-empty object ends. */ + simdjson_warn_unused simdjson_inline error_code visit_object_end(json_iterator &iter) noexcept; + /** Called when an empty object is found. */ + simdjson_warn_unused simdjson_inline error_code visit_empty_object(json_iterator &iter) noexcept; - /** - * Called when a string, number, boolean or null is found. - */ - simdjson_warn_unused simdjson_inline error_code visit_primitive(json_iterator& iter, const uint8_t* value) noexcept; - /** - * Called when a string, number, boolean or null is found at the top level of a document (i.e. - * when there is no array or object and the entire document is a single string, number, boolean or - * null. - * - * This is separate from primitive() because simdjson's normal primitive parsing routines assume - * there is at least one more token after the value, which is only true in an array or object. - */ - simdjson_warn_unused simdjson_inline error_code visit_root_primitive(json_iterator& iter, const uint8_t* value) noexcept; + /** + * Called when a string, number, boolean or null is found. + */ + simdjson_warn_unused simdjson_inline error_code visit_primitive(json_iterator &iter, const uint8_t *value) noexcept; + /** + * Called when a string, number, boolean or null is found at the top level of a document (i.e. + * when there is no array or object and the entire document is a single string, number, boolean or + * null. + * + * This is separate from primitive() because simdjson's normal primitive parsing routines assume + * there is at least one more token after the value, which is only true in an array or object. + */ + simdjson_warn_unused simdjson_inline error_code visit_root_primitive(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_string(json_iterator& iter, const uint8_t* value, bool key = false) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_number(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_true_atom(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_false_atom(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_null_atom(json_iterator& iter, const uint8_t* value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_string(json_iterator &iter, const uint8_t *value, bool key = false) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_number(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_true_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_false_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_null_atom(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_string(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_number(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_true_atom(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_false_atom(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_null_atom(json_iterator& iter, const uint8_t* value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_string(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_number(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_true_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept; - /** Called each time a new field or element in an array or object is found. */ - simdjson_warn_unused simdjson_inline error_code increment_count(json_iterator& iter) noexcept; + /** Called each time a new field or element in an array or object is found. */ + simdjson_warn_unused simdjson_inline error_code increment_count(json_iterator &iter) noexcept; - /** Next location to write to tape */ - tape_writer tape; - private: - /** Next write location in the string buf for stage 2 parsing */ - uint8_t* current_string_buf_loc; + /** Next location to write to tape */ + tape_writer tape; +private: + /** Next write location in the string buf for stage 2 parsing */ + uint8_t *current_string_buf_loc; - simdjson_inline tape_builder(dom::document& doc) noexcept; + simdjson_inline tape_builder(dom::document &doc) noexcept; - simdjson_inline uint32_t next_tape_index(json_iterator& iter) const noexcept; - simdjson_inline void start_container(json_iterator& iter) noexcept; - simdjson_warn_unused simdjson_inline error_code end_container(json_iterator& iter, internal::tape_type start, internal::tape_type end) noexcept; - simdjson_warn_unused simdjson_inline error_code empty_container(json_iterator& iter, internal::tape_type start, internal::tape_type end) noexcept; - simdjson_inline uint8_t* on_start_string(json_iterator& iter) noexcept; - simdjson_inline void on_end_string(uint8_t* dst) noexcept; - }; // struct tape_builder + simdjson_inline uint32_t next_tape_index(json_iterator &iter) const noexcept; + simdjson_inline void start_container(json_iterator &iter) noexcept; + simdjson_warn_unused simdjson_inline error_code end_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept; + simdjson_warn_unused simdjson_inline error_code empty_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept; + simdjson_inline uint8_t *on_start_string(json_iterator &iter) noexcept; + simdjson_inline void on_end_string(uint8_t *dst) noexcept; +}; // struct tape_builder - template - simdjson_warn_unused simdjson_inline error_code tape_builder::parse_document( - dom_parser_implementation& dom_parser, - dom::document& doc) noexcept { - dom_parser.doc = &doc; - json_iterator iter(dom_parser, STREAMING ? dom_parser.next_structural_index : 0); - tape_builder builder(doc); - return iter.walk_document(builder); - } +template +simdjson_warn_unused simdjson_inline error_code tape_builder::parse_document( + dom_parser_implementation &dom_parser, + dom::document &doc) noexcept { + dom_parser.doc = &doc; + json_iterator iter(dom_parser, STREAMING ? dom_parser.next_structural_index : 0); + tape_builder builder(doc); + return iter.walk_document(builder); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_primitive(json_iterator& iter, const uint8_t* value) noexcept { - return iter.visit_root_primitive(*this, value); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_primitive(json_iterator& iter, const uint8_t* value) noexcept { - return iter.visit_primitive(*this, value); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_object(json_iterator& iter) noexcept { - return empty_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_array(json_iterator& iter) noexcept { - return empty_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_primitive(json_iterator &iter, const uint8_t *value) noexcept { + return iter.visit_root_primitive(*this, value); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_primitive(json_iterator &iter, const uint8_t *value) noexcept { + return iter.visit_primitive(*this, value); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_object(json_iterator &iter) noexcept { + return empty_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_array(json_iterator &iter) noexcept { + return empty_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_start(json_iterator& iter) noexcept { - start_container(iter); - return SUCCESS; - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_start(json_iterator& iter) noexcept { - start_container(iter); - return SUCCESS; - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_start(json_iterator& iter) noexcept { - start_container(iter); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_start(json_iterator &iter) noexcept { + start_container(iter); + return SUCCESS; +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_start(json_iterator &iter) noexcept { + start_container(iter); + return SUCCESS; +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_start(json_iterator &iter) noexcept { + start_container(iter); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_end(json_iterator& iter) noexcept { - return end_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_end(json_iterator& iter) noexcept { - return end_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_end(json_iterator& iter) noexcept { - constexpr uint32_t start_tape_index = 0; - tape.append(start_tape_index, internal::tape_type::ROOT); - tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter), internal::tape_type::ROOT); - return SUCCESS; - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_key(json_iterator& iter, const uint8_t* key) noexcept { - return visit_string(iter, key, true); - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_end(json_iterator &iter) noexcept { + return end_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_end(json_iterator &iter) noexcept { + return end_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_end(json_iterator &iter) noexcept { + constexpr uint32_t start_tape_index = 0; + tape.append(start_tape_index, internal::tape_type::ROOT); + tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter), internal::tape_type::ROOT); + return SUCCESS; +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_key(json_iterator &iter, const uint8_t *key) noexcept { + return visit_string(iter, key, true); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::increment_count(json_iterator& iter) noexcept { - iter.dom_parser.open_containers[iter.depth].count++; // we have a key value pair in the object at parser.dom_parser.depth - 1 - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::increment_count(json_iterator &iter) noexcept { + iter.dom_parser.open_containers[iter.depth].count++; // we have a key value pair in the object at parser.dom_parser.depth - 1 + return SUCCESS; +} - simdjson_inline tape_builder::tape_builder(dom::document& doc) noexcept : tape{ doc.tape.get() }, current_string_buf_loc{ doc.string_buf.get() } {} +simdjson_inline tape_builder::tape_builder(dom::document &doc) noexcept : tape{doc.tape.get()}, current_string_buf_loc{doc.string_buf.get()} {} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_string(json_iterator& iter, const uint8_t* value, bool key) noexcept { - iter.log_value(key ? "key" : "string"); - uint8_t* dst = on_start_string(iter); - dst = stringparsing::parse_string(value + 1, dst, false); // We do not allow replacement when the escape characters are invalid. - if (dst == nullptr) { - iter.log_error("Invalid escape in string"); - return STRING_ERROR; - } - on_end_string(dst); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_string(json_iterator &iter, const uint8_t *value, bool key) noexcept { + iter.log_value(key ? "key" : "string"); + uint8_t *dst = on_start_string(iter); + dst = stringparsing::parse_string(value+1, dst, false); // We do not allow replacement when the escape characters are invalid. + if (dst == nullptr) { + iter.log_error("Invalid escape in string"); + return STRING_ERROR; + } + on_end_string(dst); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_string(json_iterator& iter, const uint8_t* value) noexcept { - return visit_string(iter, value); - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_string(json_iterator &iter, const uint8_t *value) noexcept { + return visit_string(iter, value); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_number(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("number"); - return numberparsing::parse_number(value, tape); - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_number(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("number"); + return numberparsing::parse_number(value, tape); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_number(json_iterator& iter, const uint8_t* value) noexcept { - // - // We need to make a copy to make sure that the string is space terminated. - // This is not about padding the input, which should already padded up - // to len + SIMDJSON_PADDING. However, we have no control at this stage - // on how the padding was done. What if the input string was padded with nulls? - // It is quite common for an input string to have an extra null character (C string). - // We do not want to allow 9\0 (where \0 is the null character) inside a JSON - // document, but the string "9\0" by itself is fine. So we make a copy and - // pad the input with spaces when we know that there is just one input element. - // This copy is relatively expensive, but it will almost never be called in - // practice unless you are in the strange scenario where you have many JSON - // documents made of single atoms. - // - std::unique_ptrcopy(new (std::nothrow) uint8_t[iter.remaining_len() + SIMDJSON_PADDING]); - if (copy.get() == nullptr) { return MEMALLOC; } - std::memcpy(copy.get(), value, iter.remaining_len()); - std::memset(copy.get() + iter.remaining_len(), ' ', SIMDJSON_PADDING); - error_code error = visit_number(iter, copy.get()); - return error; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_number(json_iterator &iter, const uint8_t *value) noexcept { + // + // We need to make a copy to make sure that the string is space terminated. + // This is not about padding the input, which should already padded up + // to len + SIMDJSON_PADDING. However, we have no control at this stage + // on how the padding was done. What if the input string was padded with nulls? + // It is quite common for an input string to have an extra null character (C string). + // We do not want to allow 9\0 (where \0 is the null character) inside a JSON + // document, but the string "9\0" by itself is fine. So we make a copy and + // pad the input with spaces when we know that there is just one input element. + // This copy is relatively expensive, but it will almost never be called in + // practice unless you are in the strange scenario where you have many JSON + // documents made of single atoms. + // + std::unique_ptrcopy(new (std::nothrow) uint8_t[iter.remaining_len() + SIMDJSON_PADDING]); + if (copy.get() == nullptr) { return MEMALLOC; } + std::memcpy(copy.get(), value, iter.remaining_len()); + std::memset(copy.get() + iter.remaining_len(), ' ', SIMDJSON_PADDING); + error_code error = visit_number(iter, copy.get()); + return error; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_true_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("true"); - if (!atomparsing::is_valid_true_atom(value)) { return T_ATOM_ERROR; } - tape.append(0, internal::tape_type::TRUE_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_true_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("true"); + if (!atomparsing::is_valid_true_atom(value)) { return T_ATOM_ERROR; } + tape.append(0, internal::tape_type::TRUE_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_true_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("true"); - if (!atomparsing::is_valid_true_atom(value, iter.remaining_len())) { return T_ATOM_ERROR; } - tape.append(0, internal::tape_type::TRUE_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_true_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("true"); + if (!atomparsing::is_valid_true_atom(value, iter.remaining_len())) { return T_ATOM_ERROR; } + tape.append(0, internal::tape_type::TRUE_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_false_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("false"); - if (!atomparsing::is_valid_false_atom(value)) { return F_ATOM_ERROR; } - tape.append(0, internal::tape_type::FALSE_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_false_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("false"); + if (!atomparsing::is_valid_false_atom(value)) { return F_ATOM_ERROR; } + tape.append(0, internal::tape_type::FALSE_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_false_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("false"); - if (!atomparsing::is_valid_false_atom(value, iter.remaining_len())) { return F_ATOM_ERROR; } - tape.append(0, internal::tape_type::FALSE_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("false"); + if (!atomparsing::is_valid_false_atom(value, iter.remaining_len())) { return F_ATOM_ERROR; } + tape.append(0, internal::tape_type::FALSE_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_null_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("null"); - if (!atomparsing::is_valid_null_atom(value)) { return N_ATOM_ERROR; } - tape.append(0, internal::tape_type::NULL_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_null_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("null"); + if (!atomparsing::is_valid_null_atom(value)) { return N_ATOM_ERROR; } + tape.append(0, internal::tape_type::NULL_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_null_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("null"); - if (!atomparsing::is_valid_null_atom(value, iter.remaining_len())) { return N_ATOM_ERROR; } - tape.append(0, internal::tape_type::NULL_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("null"); + if (!atomparsing::is_valid_null_atom(value, iter.remaining_len())) { return N_ATOM_ERROR; } + tape.append(0, internal::tape_type::NULL_VALUE); + return SUCCESS; +} - // private: +// private: - simdjson_inline uint32_t tape_builder::next_tape_index(json_iterator& iter) const noexcept { - return uint32_t(tape.next_tape_loc - iter.dom_parser.doc->tape.get()); - } +simdjson_inline uint32_t tape_builder::next_tape_index(json_iterator &iter) const noexcept { + return uint32_t(tape.next_tape_loc - iter.dom_parser.doc->tape.get()); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::empty_container(json_iterator& iter, internal::tape_type start, internal::tape_type end) noexcept { - auto start_index = next_tape_index(iter); - tape.append(start_index + 2, start); - tape.append(start_index, end); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::empty_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept { + auto start_index = next_tape_index(iter); + tape.append(start_index+2, start); + tape.append(start_index, end); + return SUCCESS; +} - simdjson_inline void tape_builder::start_container(json_iterator& iter) noexcept { - iter.dom_parser.open_containers[iter.depth].tape_index = next_tape_index(iter); - iter.dom_parser.open_containers[iter.depth].count = 0; - tape.skip(); // We don't actually *write* the start element until the end. - } +simdjson_inline void tape_builder::start_container(json_iterator &iter) noexcept { + iter.dom_parser.open_containers[iter.depth].tape_index = next_tape_index(iter); + iter.dom_parser.open_containers[iter.depth].count = 0; + tape.skip(); // We don't actually *write* the start element until the end. +} - simdjson_warn_unused simdjson_inline error_code tape_builder::end_container(json_iterator& iter, internal::tape_type start, internal::tape_type end) noexcept { - // Write the ending tape element, pointing at the start location - const uint32_t start_tape_index = iter.dom_parser.open_containers[iter.depth].tape_index; - tape.append(start_tape_index, end); - // Write the start tape element, pointing at the end location (and including count) - // count can overflow if it exceeds 24 bits... so we saturate - // the convention being that a cnt of 0xffffff or more is undetermined in value (>= 0xffffff). - const uint32_t count = iter.dom_parser.open_containers[iter.depth].count; - const uint32_t cntsat = count > 0xFFFFFF ? 0xFFFFFF : count; - tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter) | (uint64_t(cntsat) << 32), start); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::end_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept { + // Write the ending tape element, pointing at the start location + const uint32_t start_tape_index = iter.dom_parser.open_containers[iter.depth].tape_index; + tape.append(start_tape_index, end); + // Write the start tape element, pointing at the end location (and including count) + // count can overflow if it exceeds 24 bits... so we saturate + // the convention being that a cnt of 0xffffff or more is undetermined in value (>= 0xffffff). + const uint32_t count = iter.dom_parser.open_containers[iter.depth].count; + const uint32_t cntsat = count > 0xFFFFFF ? 0xFFFFFF : count; + tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter) | (uint64_t(cntsat) << 32), start); + return SUCCESS; +} - simdjson_inline uint8_t* tape_builder::on_start_string(json_iterator& iter) noexcept { - // we advance the point, accounting for the fact that we have a NULL termination - tape.append(current_string_buf_loc - iter.dom_parser.doc->string_buf.get(), internal::tape_type::STRING); - return current_string_buf_loc + sizeof(uint32_t); - } +simdjson_inline uint8_t *tape_builder::on_start_string(json_iterator &iter) noexcept { + // we advance the point, accounting for the fact that we have a NULL termination + tape.append(current_string_buf_loc - iter.dom_parser.doc->string_buf.get(), internal::tape_type::STRING); + return current_string_buf_loc + sizeof(uint32_t); +} - simdjson_inline void tape_builder::on_end_string(uint8_t* dst) noexcept { - uint32_t str_length = uint32_t(dst - (current_string_buf_loc + sizeof(uint32_t))); - // TODO check for overflow in case someone has a crazy string (>=4GB?) - // But only add the overflow check when the document itself exceeds 4GB - // Currently unneeded because we refuse to parse docs larger or equal to 4GB. - memcpy(current_string_buf_loc, &str_length, sizeof(uint32_t)); - // NULL termination is still handy if you expect all your strings to - // be NULL terminated? It comes at a small cost - *dst = 0; - current_string_buf_loc = dst + 1; - } +simdjson_inline void tape_builder::on_end_string(uint8_t *dst) noexcept { + uint32_t str_length = uint32_t(dst - (current_string_buf_loc + sizeof(uint32_t))); + // TODO check for overflow in case someone has a crazy string (>=4GB?) + // But only add the overflow check when the document itself exceeds 4GB + // Currently unneeded because we refuse to parse docs larger or equal to 4GB. + memcpy(current_string_buf_loc, &str_length, sizeof(uint32_t)); + // NULL termination is still handy if you expect all your strings to + // be NULL terminated? It comes at a small cost + *dst = 0; + current_string_buf_loc = dst + 1; +} - } // namespace stage2 - } // unnamed namespace - } // namespace icelake +} // namespace stage2 +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_BUILDER_H @@ -26467,98 +28030,98 @@ namespace simdjson { // namespace simdjson { - namespace icelake { +namespace icelake { - simdjson_warn_unused error_code implementation::create_dom_parser_implementation( - size_t capacity, - size_t max_depth, - std::unique_ptr& dst - ) const noexcept { - dst.reset(new (std::nothrow) dom_parser_implementation()); - if (!dst) { return MEMALLOC; } - if (auto err = dst->set_capacity(capacity)) - return err; - if (auto err = dst->set_max_depth(max_depth)) - return err; - return SUCCESS; - } +simdjson_warn_unused error_code implementation::create_dom_parser_implementation( + size_t capacity, + size_t max_depth, + std::unique_ptr& dst +) const noexcept { + dst.reset( new (std::nothrow) dom_parser_implementation() ); + if (!dst) { return MEMALLOC; } + if (auto err = dst->set_capacity(capacity)) + return err; + if (auto err = dst->set_max_depth(max_depth)) + return err; + return SUCCESS; +} - namespace { +namespace { - using namespace simd; +using namespace simd; - // This identifies structural characters (comma, colon, braces, brackets), - // and ASCII white-space ('\r','\n','\t',' '). - simdjson_inline json_character_block json_character_block::classify(const simd::simd8x64& in) { - // These lookups rely on the fact that anything < 127 will match the lower 4 bits, which is why - // we can't use the generic lookup_16. - const auto whitespace_table = simd8::repeat_16(' ', 100, 100, 100, 17, 100, 113, 2, 100, '\t', '\n', 112, 100, '\r', 100, 100); +// This identifies structural characters (comma, colon, braces, brackets), +// and ASCII white-space ('\r','\n','\t',' '). +simdjson_inline json_character_block json_character_block::classify(const simd::simd8x64& in) { + // These lookups rely on the fact that anything < 127 will match the lower 4 bits, which is why + // we can't use the generic lookup_16. + const auto whitespace_table = simd8::repeat_16(' ', 100, 100, 100, 17, 100, 113, 2, 100, '\t', '\n', 112, 100, '\r', 100, 100); - // The 6 operators (:,[]{}) have these values: - // - // , 2C - // : 3A - // [ 5B - // { 7B - // ] 5D - // } 7D - // - // If you use | 0x20 to turn [ and ] into { and }, the lower 4 bits of each character is unique. - // We exploit this, using a simd 4-bit lookup to tell us which character match against, and then - // match it (against | 0x20). - // - // To prevent recognizing other characters, everything else gets compared with 0, which cannot - // match due to the | 0x20. - // - // NOTE: Due to the | 0x20, this ALSO treats and (control characters 0C and 1A) like , - // and :. This gets caught in stage 2, which checks the actual character to ensure the right - // operators are in the right places. - const auto op_table = simd8::repeat_16( - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, ':', '{', // : = 3A, [ = 5B, { = 7B - ',', '}', 0, 0 // , = 2C, ] = 5D, } = 7D - ); + // The 6 operators (:,[]{}) have these values: + // + // , 2C + // : 3A + // [ 5B + // { 7B + // ] 5D + // } 7D + // + // If you use | 0x20 to turn [ and ] into { and }, the lower 4 bits of each character is unique. + // We exploit this, using a simd 4-bit lookup to tell us which character match against, and then + // match it (against | 0x20). + // + // To prevent recognizing other characters, everything else gets compared with 0, which cannot + // match due to the | 0x20. + // + // NOTE: Due to the | 0x20, this ALSO treats and (control characters 0C and 1A) like , + // and :. This gets caught in stage 2, which checks the actual character to ensure the right + // operators are in the right places. + const auto op_table = simd8::repeat_16( + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, ':', '{', // : = 3A, [ = 5B, { = 7B + ',', '}', 0, 0 // , = 2C, ] = 5D, } = 7D + ); - // We compute whitespace and op separately. If later code only uses one or the - // other, given the fact that all functions are aggressively inlined, we can - // hope that useless computations will be omitted. This is namely case when - // minifying (we only need whitespace). + // We compute whitespace and op separately. If later code only uses one or the + // other, given the fact that all functions are aggressively inlined, we can + // hope that useless computations will be omitted. This is namely case when + // minifying (we only need whitespace). - const uint64_t whitespace = in.eq({ - _mm512_shuffle_epi8(whitespace_table, in.chunks[0]) - }); - // Turn [ and ] into { and } - const simd8x64 curlified{ - in.chunks[0] | 0x20 - }; - const uint64_t op = curlified.eq({ - _mm512_shuffle_epi8(op_table, in.chunks[0]) - }); + const uint64_t whitespace = in.eq({ + _mm512_shuffle_epi8(whitespace_table, in.chunks[0]) + }); + // Turn [ and ] into { and } + const simd8x64 curlified{ + in.chunks[0] | 0x20 + }; + const uint64_t op = curlified.eq({ + _mm512_shuffle_epi8(op_table, in.chunks[0]) + }); - return { whitespace, op }; - } + return { whitespace, op }; +} - simdjson_inline bool is_ascii(const simd8x64& input) { - return input.reduce_or().is_ascii(); - } +simdjson_inline bool is_ascii(const simd8x64& input) { + return input.reduce_or().is_ascii(); +} - simdjson_unused simdjson_inline simd8 must_be_continuation(const simd8 prev1, const simd8 prev2, const simd8 prev3) { - simd8 is_second_byte = prev1.saturating_sub(0xc0u - 1); // Only 11______ will be > 0 - simd8 is_third_byte = prev2.saturating_sub(0xe0u - 1); // Only 111_____ will be > 0 - simd8 is_fourth_byte = prev3.saturating_sub(0xf0u - 1); // Only 1111____ will be > 0 - // Caller requires a bool (all 1's). All values resulting from the subtraction will be <= 64, so signed comparison is fine. - return simd8(is_second_byte | is_third_byte | is_fourth_byte) > int8_t(0); - } +simdjson_unused simdjson_inline simd8 must_be_continuation(const simd8 prev1, const simd8 prev2, const simd8 prev3) { + simd8 is_second_byte = prev1.saturating_sub(0xc0u-1); // Only 11______ will be > 0 + simd8 is_third_byte = prev2.saturating_sub(0xe0u-1); // Only 111_____ will be > 0 + simd8 is_fourth_byte = prev3.saturating_sub(0xf0u-1); // Only 1111____ will be > 0 + // Caller requires a bool (all 1's). All values resulting from the subtraction will be <= 64, so signed comparison is fine. + return simd8(is_second_byte | is_third_byte | is_fourth_byte) > int8_t(0); +} - simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { - simd8 is_third_byte = prev2.saturating_sub(0xe0u - 0x80); // Only 111_____ will be >= 0x80 - simd8 is_fourth_byte = prev3.saturating_sub(0xf0u - 0x80); // Only 1111____ will be >= 0x80 - return is_third_byte | is_fourth_byte; - } +simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { + simd8 is_third_byte = prev2.saturating_sub(0xe0u-0x80); // Only 111_____ will be >= 0x80 + simd8 is_fourth_byte = prev3.saturating_sub(0xf0u-0x80); // Only 1111____ will be >= 0x80 + return is_third_byte | is_fourth_byte; +} - } // unnamed namespace - } // namespace icelake +} // unnamed namespace +} // namespace icelake } // namespace simdjson /** @@ -26566,49 +28129,43 @@ namespace simdjson { * naked intrinsics. * TODO: make this code more elegant. */ - // Under GCC 12, the intrinsic _mm512_extracti32x4_epi32 may generate 'maybe uninitialized'. - // as a workaround, we disable warnings within the following function. +// Under GCC 12, the intrinsic _mm512_extracti32x4_epi32 may generate 'maybe uninitialized'. +// as a workaround, we disable warnings within the following function. SIMDJSON_PUSH_DISABLE_ALL_WARNINGS -namespace simdjson { - namespace icelake { - namespace { - namespace stage1 { - simdjson_inline void bit_indexer::write(uint32_t idx, uint64_t bits) { - // In some instances, the next branch is expensive because it is mispredicted. - // Unfortunately, in other cases, - // it helps tremendously. - if (bits == 0) { return; } +namespace simdjson { namespace icelake { namespace { namespace stage1 { +simdjson_inline void bit_indexer::write(uint32_t idx, uint64_t bits) { + // In some instances, the next branch is expensive because it is mispredicted. + // Unfortunately, in other cases, + // it helps tremendously. + if (bits == 0) { return; } - const __m512i indexes = _mm512_maskz_compress_epi8(bits, _mm512_set_epi32( - 0x3f3e3d3c, 0x3b3a3938, 0x37363534, 0x33323130, - 0x2f2e2d2c, 0x2b2a2928, 0x27262524, 0x23222120, - 0x1f1e1d1c, 0x1b1a1918, 0x17161514, 0x13121110, - 0x0f0e0d0c, 0x0b0a0908, 0x07060504, 0x03020100 - )); - const __m512i start_index = _mm512_set1_epi32(idx); + const __m512i indexes = _mm512_maskz_compress_epi8(bits, _mm512_set_epi32( + 0x3f3e3d3c, 0x3b3a3938, 0x37363534, 0x33323130, + 0x2f2e2d2c, 0x2b2a2928, 0x27262524, 0x23222120, + 0x1f1e1d1c, 0x1b1a1918, 0x17161514, 0x13121110, + 0x0f0e0d0c, 0x0b0a0908, 0x07060504, 0x03020100 + )); + const __m512i start_index = _mm512_set1_epi32(idx); - const auto count = count_ones(bits); - __m512i t0 = _mm512_cvtepu8_epi32(_mm512_castsi512_si128(indexes)); - _mm512_storeu_si512(this->tail, _mm512_add_epi32(t0, start_index)); + const auto count = count_ones(bits); + __m512i t0 = _mm512_cvtepu8_epi32(_mm512_castsi512_si128(indexes)); + _mm512_storeu_si512(this->tail, _mm512_add_epi32(t0, start_index)); - if (count > 16) { - const __m512i t1 = _mm512_cvtepu8_epi32(_mm512_extracti32x4_epi32(indexes, 1)); - _mm512_storeu_si512(this->tail + 16, _mm512_add_epi32(t1, start_index)); - if (count > 32) { - const __m512i t2 = _mm512_cvtepu8_epi32(_mm512_extracti32x4_epi32(indexes, 2)); - _mm512_storeu_si512(this->tail + 32, _mm512_add_epi32(t2, start_index)); - if (count > 48) { - const __m512i t3 = _mm512_cvtepu8_epi32(_mm512_extracti32x4_epi32(indexes, 3)); - _mm512_storeu_si512(this->tail + 48, _mm512_add_epi32(t3, start_index)); - } - } - } - this->tail += count; - } - } + if(count > 16) { + const __m512i t1 = _mm512_cvtepu8_epi32(_mm512_extracti32x4_epi32(indexes, 1)); + _mm512_storeu_si512(this->tail + 16, _mm512_add_epi32(t1, start_index)); + if(count > 32) { + const __m512i t2 = _mm512_cvtepu8_epi32(_mm512_extracti32x4_epi32(indexes, 2)); + _mm512_storeu_si512(this->tail + 32, _mm512_add_epi32(t2, start_index)); + if(count > 48) { + const __m512i t3 = _mm512_cvtepu8_epi32(_mm512_extracti32x4_epi32(indexes, 3)); + _mm512_storeu_si512(this->tail + 48, _mm512_add_epi32(t3, start_index)); } + } } + this->tail += count; } +}}}} SIMDJSON_POP_DISABLE_WARNINGS // @@ -26619,45 +28176,46 @@ SIMDJSON_POP_DISABLE_WARNINGS // Implementation-specific overrides // namespace simdjson { - namespace icelake { +namespace icelake { - simdjson_warn_unused error_code implementation::minify(const uint8_t* buf, size_t len, uint8_t* dst, size_t& dst_len) const noexcept { - return icelake::stage1::json_minifier::minify<128>(buf, len, dst, dst_len); - } +simdjson_warn_unused error_code implementation::minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept { + return icelake::stage1::json_minifier::minify<128>(buf, len, dst, dst_len); +} - simdjson_warn_unused error_code dom_parser_implementation::stage1(const uint8_t* _buf, size_t _len, stage1_mode streaming) noexcept { - this->buf = _buf; - this->len = _len; - return icelake::stage1::json_structural_indexer::index<128>(_buf, _len, *this, streaming); - } +simdjson_warn_unused error_code dom_parser_implementation::stage1(const uint8_t *_buf, size_t _len, stage1_mode streaming) noexcept { + this->buf = _buf; + this->len = _len; + return icelake::stage1::json_structural_indexer::index<128>(_buf, _len, *this, streaming); +} - simdjson_warn_unused bool implementation::validate_utf8(const char* buf, size_t len) const noexcept { - return icelake::stage1::generic_validate_utf8(buf, len); - } +simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept { + return icelake::stage1::generic_validate_utf8(buf,len); +} - simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document& _doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); - } +simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept { + return stage2::tape_builder::parse_document(*this, _doc); +} - simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document& _doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); - } +simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept { + return stage2::tape_builder::parse_document(*this, _doc); +} - simdjson_warn_unused uint8_t* dom_parser_implementation::parse_string(const uint8_t* src, uint8_t* dst, bool replacement_char) const noexcept { - return icelake::stringparsing::parse_string(src, dst, replacement_char); - } +SIMDJSON_NO_SANITIZE_MEMORY +simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool replacement_char) const noexcept { + return icelake::stringparsing::parse_string(src, dst, replacement_char); +} - simdjson_warn_unused uint8_t* dom_parser_implementation::parse_wobbly_string(const uint8_t* src, uint8_t* dst) const noexcept { - return icelake::stringparsing::parse_wobbly_string(src, dst); - } +simdjson_warn_unused uint8_t *dom_parser_implementation::parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept { + return icelake::stringparsing::parse_wobbly_string(src, dst); +} - simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t* _buf, size_t _len, dom::document& _doc) noexcept { - auto error = stage1(_buf, _len, stage1_mode::regular); - if (error) { return error; } - return stage2(_doc); - } +simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept { + auto error = stage1(_buf, _len, stage1_mode::regular); + if (error) { return error; } + return stage2(_doc); +} - } // namespace icelake +} // namespace icelake } // namespace simdjson /* including simdjson/icelake/end.h: #include */ @@ -26706,21 +28264,21 @@ SIMDJSON_UNTARGET_REGION /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - /** - * Implementation for ALTIVEC (PPC64). - */ - namespace ppc64 { +/** + * Implementation for ALTIVEC (PPC64). + */ +namespace ppc64 { - class implementation; +class implementation; - namespace { - namespace simd { - template struct simd8; - template struct simd8x64; - } // namespace simd - } // unnamed namespace +namespace { +namespace simd { +template struct simd8; +template struct simd8x64; +} // namespace simd +} // unnamed namespace - } // namespace ppc64 +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_PPC64_BASE_H @@ -26761,73 +28319,73 @@ static_assert(sizeof(__vector unsigned char) <= simdjson::SIMDJSON_PADDING, "ins /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { - namespace { +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) { +// 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; + 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); + 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 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) { +/* 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; + 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); + 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 - } +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); - } +simdjson_inline int count_ones(uint64_t input_num) { + return __builtin_popcountll(input_num); +} #endif - simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, - uint64_t* result) { +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { #if SIMDJSON_REGULAR_VISUAL_STUDIO - * result = value1 + value2; - return *result < value1; + *result = value1 + value2; + return *result < value1; #else - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); #endif - } +} - } // unnamed namespace - } // namespace ppc64 +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_PPC64_BITMANIPULATION_H @@ -26842,41 +28400,41 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { - namespace { +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; - } +// +// 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 +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif @@ -26901,51 +28459,57 @@ namespace simdjson { #endif namespace simdjson { - namespace ppc64 { - namespace numberparsing { +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)); +// 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); + val = bswap_64(val); #elif defined(__FreeBSD__) - val = bswap64(val); + val = bswap64(val); #endif #endif - val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8; - val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16; - return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32); - } + 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; +/** @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; + // 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 + 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); + __uint128_t r = (static_cast<__uint128_t>(value1)) * value2; + answer.low = uint64_t(r); + answer.high = uint64_t(r >> 64); #endif - return answer; - } + return answer; +} - } // namespace numberparsing - } // namespace ppc64 +} // 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 */ @@ -26963,469 +28527,464 @@ namespace simdjson { #include namespace simdjson { - namespace ppc64 { - namespace { - namespace simd { +namespace ppc64 { +namespace { +namespace simd { - using __m128i = __vector unsigned char; +using __m128i = __vector unsigned char; - template struct base { - __m128i value; +template struct base { + __m128i value; - // Zero constructor - simdjson_inline base() : value{ __m128i() } {} + // Zero constructor + simdjson_inline base() : value{__m128i()} {} - // Conversion from SIMD register - simdjson_inline base(const __m128i _value) : value(_value) {} + // 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; } + // 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(this); - *this_cast = *this_cast | other; - return *this_cast; - } - simdjson_inline Child& operator&=(const Child other) { - auto this_cast = static_cast(this); - *this_cast = *this_cast & other; - return *this_cast; - } - simdjson_inline Child& operator^=(const Child other) { - auto this_cast = static_cast(this); - *this_cast = *this_cast ^ other; - return *this_cast; - } - }; + // 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(this); + *this_cast = *this_cast | other; + return *this_cast; + } + simdjson_inline Child &operator&=(const Child other) { + auto this_cast = static_cast(this); + *this_cast = *this_cast & other; + return *this_cast; + } + simdjson_inline Child &operator^=(const Child other) { + auto this_cast = static_cast(this); + *this_cast = *this_cast ^ other; + return *this_cast; + } +}; - template > - struct base8 : base> { - typedef uint16_t bitmask_t; - typedef uint32_t bitmask2_t; +template > +struct base8 : base> { + typedef uint16_t bitmask_t; + typedef uint32_t bitmask2_t; - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m128i _value) : base>(_value) {} + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m128i _value) : base>(_value) {} - friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { - return (__m128i)vec_cmpeq(lhs.value, (__m128i)rhs); - } + friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { + return (__m128i)vec_cmpeq(lhs.value, (__m128i)rhs); + } - static const int SIZE = sizeof(base>::value); + static const int SIZE = sizeof(base>::value); - template - simdjson_inline simd8 prev(simd8 prev_chunk) const { - __m128i chunk = this->value; + template + simdjson_inline simd8 prev(simd8 prev_chunk) const { + __m128i chunk = this->value; #ifdef __LITTLE_ENDIAN__ - chunk = (__m128i)vec_reve(this->value); - prev_chunk = (__m128i)vec_reve((__m128i)prev_chunk); + 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); + chunk = (__m128i)vec_sld((__m128i)prev_chunk, (__m128i)chunk, 16 - N); #ifdef __LITTLE_ENDIAN__ - chunk = (__m128i)vec_reve((__m128i)chunk); + chunk = (__m128i)vec_reve((__m128i)chunk); #endif - return chunk; - } - }; + return chunk; + } +}; - // SIMD byte mask type (returned by things like eq and gt) - template <> struct simd8 : base8 { - static simdjson_inline simd8 splat(bool _value) { - return (__m128i)vec_splats((unsigned char)(-(!!_value))); - } +// SIMD byte mask type (returned by things like eq and gt) +template <> struct simd8 : base8 { + static simdjson_inline simd8 splat(bool _value) { + return (__m128i)vec_splats((unsigned char)(-(!!_value))); + } - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m128i _value) - : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) - : base8(splat(_value)) {} + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m128i _value) + : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) + : base8(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 }; + 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)); + result = ((__vector unsigned long long)vec_vbpermq((__m128i)this->value, + (__m128i)perm_mask)); #ifdef __LITTLE_ENDIAN__ - return static_cast(result[1]); + return static_cast(result[1]); #else - return static_cast(result[0]); + return static_cast(result[0]); #endif - } - simdjson_inline bool any() const { - return !vec_all_eq(this->value, (__m128i)vec_splats(0)); - } - simdjson_inline simd8 operator~() const { - return this->value ^ (__m128i)splat(true); - } - }; + } + simdjson_inline bool any() const { + return !vec_all_eq(this->value, (__m128i)vec_splats(0)); + } + simdjson_inline simd8 operator~() const { + return this->value ^ (__m128i)splat(true); + } +}; - template struct base8_numeric : base8 { - static simdjson_inline simd8 splat(T value) { - (void)value; - return (__m128i)vec_splats(value); - } - static simdjson_inline simd8 zero() { return splat(0); } - static simdjson_inline simd8 load(const T values[16]) { - return (__m128i)(vec_vsx_ld(0, reinterpret_cast(values))); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdjson_inline simd8 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(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, - v14, v15); - } +template struct base8_numeric : base8 { + static simdjson_inline simd8 splat(T value) { + (void)value; + return (__m128i)vec_splats(value); + } + static simdjson_inline simd8 zero() { return splat(0); } + static simdjson_inline simd8 load(const T values[16]) { + return (__m128i)(vec_vsx_ld(0, reinterpret_cast(values))); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, + v14, v15); + } - simdjson_inline base8_numeric() : base8() {} - simdjson_inline base8_numeric(const __m128i _value) - : base8(_value) {} + simdjson_inline base8_numeric() : base8() {} + simdjson_inline base8_numeric(const __m128i _value) + : base8(_value) {} - // Store to array - simdjson_inline void store(T dst[16]) const { - vec_vsx_st(this->value, 0, reinterpret_cast<__m128i*>(dst)); - } + // 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 operator~() const { return *this ^ 0xFFu; } + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - // Addition/subtraction are the same for signed and unsigned - simdjson_inline simd8 operator+(const simd8 other) const { - return (__m128i)((__m128i)this->value + (__m128i)other); - } - simdjson_inline simd8 operator-(const simd8 other) const { - return (__m128i)((__m128i)this->value - (__m128i)other); - } - simdjson_inline simd8& operator+=(const simd8 other) { - *this = *this + other; - return *static_cast *>(this); - } - simdjson_inline simd8& operator-=(const simd8 other) { - *this = *this - other; - return *static_cast *>(this); - } + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { + return (__m128i)((__m128i)this->value + (__m128i)other); + } + simdjson_inline simd8 operator-(const simd8 other) const { + return (__m128i)((__m128i)this->value - (__m128i)other); + } + simdjson_inline simd8 &operator+=(const simd8 other) { + *this = *this + other; + return *static_cast *>(this); + } + simdjson_inline simd8 &operator-=(const simd8 other) { + *this = *this - other; + return *static_cast *>(this); + } - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior - // for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return (__m128i)vec_perm((__m128i)lookup_table, (__m128i)lookup_table, this->value); - } + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior + // for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 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 compress(uint32_t mask) - // would be sensible, but the AVX ISA makes this kind of approach difficult. - template - 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. + // 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 compress(uint32_t mask) + // would be sensible, but the AVX ISA makes this kind of approach difficult. + template + 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] - }; + __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); + __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 }); + // 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(pshufb_combine_table + pop1 * 8)); - __m128i answer = vec_perm(pruned, (__m128i)vec_splats(0), compactmask); - vec_vsx_st(answer, 0, reinterpret_cast<__m128i*>(output)); - } + // 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(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 - simdjson_inline simd8 - 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::repeat_16( - replace0, replace1, replace2, replace3, replace4, replace5, replace6, - replace7, replace8, replace9, replace10, replace11, replace12, - replace13, replace14, replace15)); - } - }; + template + simdjson_inline simd8 + 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::repeat_16( + replace0, replace1, replace2, replace3, replace4, replace5, replace6, + replace7, replace8, replace9, replace10, replace11, replace12, + replace13, replace14, replace15)); + } +}; - // Signed bytes - template <> struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) - : base8_numeric(_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 - 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(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, - v13, v14, v15); - } +// Signed bytes +template <> struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) + : base8_numeric(_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 + 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(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, + v13, v14, v15); + } - // Order-sensitive comparisons - simdjson_inline simd8 - max_val(const simd8 other) const { - return (__m128i)vec_max((__vector signed char)this->value, + // Order-sensitive comparisons + simdjson_inline simd8 + max_val(const simd8 other) const { + return (__m128i)vec_max((__vector signed char)this->value, (__vector signed char)(__m128i)other); - } - simdjson_inline simd8 - min_val(const simd8 other) const { - return (__m128i)vec_min((__vector signed char)this->value, + } + simdjson_inline simd8 + min_val(const simd8 other) const { + return (__m128i)vec_min((__vector signed char)this->value, (__vector signed char)(__m128i)other); - } - simdjson_inline simd8 - operator>(const simd8 other) const { - return (__m128i)vec_cmpgt((__vector signed char)this->value, - (__vector signed char)(__m128i)other); - } - simdjson_inline simd8 - operator<(const simd8 other) const { - return (__m128i)vec_cmplt((__vector signed char)this->value, - (__vector signed char)(__m128i)other); - } - }; + } + simdjson_inline simd8 + operator>(const simd8 other) const { + return (__m128i)vec_cmpgt((__vector signed char)this->value, + (__vector signed char)(__m128i)other); + } + simdjson_inline simd8 + operator<(const simd8 other) const { + return (__m128i)vec_cmplt((__vector signed char)this->value, + (__vector signed char)(__m128i)other); + } +}; - // Unsigned bytes - template <> struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) - : base8_numeric(_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 - 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(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, - v13, v14, v15); - } +// Unsigned bytes +template <> struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) + : base8_numeric(_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 + 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(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, + v13, v14, v15); + } - // Saturated math - simdjson_inline simd8 - saturating_add(const simd8 other) const { - return (__m128i)vec_adds(this->value, (__m128i)other); - } - simdjson_inline simd8 - saturating_sub(const simd8 other) const { - return (__m128i)vec_subs(this->value, (__m128i)other); - } + // Saturated math + simdjson_inline simd8 + saturating_add(const simd8 other) const { + return (__m128i)vec_adds(this->value, (__m128i)other); + } + simdjson_inline simd8 + saturating_sub(const simd8 other) const { + return (__m128i)vec_subs(this->value, (__m128i)other); + } - // Order-specific operations - simdjson_inline simd8 - max_val(const simd8 other) const { - return (__m128i)vec_max(this->value, (__m128i)other); - } - simdjson_inline simd8 - min_val(const simd8 other) const { - return (__m128i)vec_min(this->value, (__m128i)other); - } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 - gt_bits(const simd8 other) const { - return this->saturating_sub(other); - } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 - lt_bits(const simd8 other) const { - return other.saturating_sub(*this); - } - simdjson_inline simd8 - operator<=(const simd8 other) const { - return other.max_val(*this) == other; - } - simdjson_inline simd8 - operator>=(const simd8 other) const { - return other.min_val(*this) == other; - } - simdjson_inline simd8 - operator>(const simd8 other) const { - return this->gt_bits(other).any_bits_set(); - } - simdjson_inline simd8 - operator<(const simd8 other) const { - return this->gt_bits(other).any_bits_set(); - } + // Order-specific operations + simdjson_inline simd8 + max_val(const simd8 other) const { + return (__m128i)vec_max(this->value, (__m128i)other); + } + simdjson_inline simd8 + min_val(const simd8 other) const { + return (__m128i)vec_min(this->value, (__m128i)other); + } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 + gt_bits(const simd8 other) const { + return this->saturating_sub(other); + } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 + lt_bits(const simd8 other) const { + return other.saturating_sub(*this); + } + simdjson_inline simd8 + operator<=(const simd8 other) const { + return other.max_val(*this) == other; + } + simdjson_inline simd8 + operator>=(const simd8 other) const { + return other.min_val(*this) == other; + } + simdjson_inline simd8 + operator>(const simd8 other) const { + return this->gt_bits(other).any_bits_set(); + } + simdjson_inline simd8 + operator<(const simd8 other) const { + return this->gt_bits(other).any_bits_set(); + } - // Bit-specific operations - simdjson_inline simd8 bits_not_set() const { - return (__m128i)vec_cmpeq(this->value, (__m128i)vec_splats(uint8_t(0))); - } - simdjson_inline simd8 bits_not_set(simd8 bits) const { - return (*this & bits).bits_not_set(); - } - simdjson_inline simd8 any_bits_set() const { - return ~this->bits_not_set(); - } - simdjson_inline simd8 any_bits_set(simd8 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 bits) const { - return vec_all_eq(vec_and(this->value, (__m128i)bits), - (__m128i)vec_splats(0)); - } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { - return !bits_not_set_anywhere(bits); - } - template simdjson_inline simd8 shr() const { - return simd8( - (__m128i)vec_sr(this->value, (__m128i)vec_splat_u8(N))); - } - template simdjson_inline simd8 shl() const { - return simd8( - (__m128i)vec_sl(this->value, (__m128i)vec_splat_u8(N))); - } - }; + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { + return (__m128i)vec_cmpeq(this->value, (__m128i)vec_splats(uint8_t(0))); + } + simdjson_inline simd8 bits_not_set(simd8 bits) const { + return (*this & bits).bits_not_set(); + } + simdjson_inline simd8 any_bits_set() const { + return ~this->bits_not_set(); + } + simdjson_inline simd8 any_bits_set(simd8 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 bits) const { + return vec_all_eq(vec_and(this->value, (__m128i)bits), + (__m128i)vec_splats(0)); + } + simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { + return !bits_not_set_anywhere(bits); + } + template simdjson_inline simd8 shr() const { + return simd8( + (__m128i)vec_sr(this->value, (__m128i)vec_splat_u8(N))); + } + template simdjson_inline simd8 shl() const { + return simd8( + (__m128i)vec_sl(this->value, (__m128i)vec_splat_u8(N))); + } +}; - template struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 4, - "PPC64 kernel should use four registers per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; +template struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 4, + "PPC64 kernel should use four registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& - operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed + simd8x64(const simd8x64 &o) = delete; // no copy allowed + simd8x64 & + operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, - const simd8 chunk2, const simd8 chunk3) - : chunks{ chunk0, chunk1, chunk2, chunk3 } {} - simdjson_inline simd8x64(const T ptr[64]) - : chunks{ simd8::load(ptr), simd8::load(ptr + 16), - simd8::load(ptr + 32), simd8::load(ptr + 48) } {} + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, + const simd8 chunk2, const simd8 chunk3) + : chunks{chunk0, chunk1, chunk2, chunk3} {} + simdjson_inline simd8x64(const T ptr[64]) + : chunks{simd8::load(ptr), simd8::load(ptr + 16), + simd8::load(ptr + 32), simd8::load(ptr + 48)} {} - simdjson_inline void store(T ptr[64]) const { - this->chunks[0].store(ptr + sizeof(simd8) * 0); - this->chunks[1].store(ptr + sizeof(simd8) * 1); - this->chunks[2].store(ptr + sizeof(simd8) * 2); - this->chunks[3].store(ptr + sizeof(simd8) * 3); - } + simdjson_inline void store(T ptr[64]) const { + this->chunks[0].store(ptr + sizeof(simd8) * 0); + this->chunks[1].store(ptr + sizeof(simd8) * 1); + this->chunks[2].store(ptr + sizeof(simd8) * 2); + this->chunks[3].store(ptr + sizeof(simd8) * 3); + } - simdjson_inline simd8 reduce_or() const { - return (this->chunks[0] | this->chunks[1]) | - (this->chunks[2] | this->chunks[3]); - } + simdjson_inline simd8 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 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 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 mask = simd8::splat(m); - return simd8x64(this->chunks[0] == mask, this->chunks[1] == mask, - this->chunks[2] == mask, this->chunks[3] == mask) - .to_bitmask(); - } + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64(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& other) const { - return simd8x64(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 eq(const simd8x64 &other) const { + return simd8x64(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 mask = simd8::splat(m); - return simd8x64(this->chunks[0] <= mask, this->chunks[1] <= mask, - this->chunks[2] <= mask, this->chunks[3] <= mask) - .to_bitmask(); - } - }; // struct simd8x64 + simdjson_inline uint64_t lteq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64(this->chunks[0] <= mask, this->chunks[1] <= mask, + this->chunks[2] <= mask, this->chunks[3] <= mask) + .to_bitmask(); + } +}; // struct simd8x64 - } // namespace simd - } // unnamed namespace - } // namespace ppc64 +} // namespace simd +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_PPC64_SIMD_INPUT_H @@ -27442,58 +29001,84 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { - namespace { +namespace ppc64 { +namespace { - using namespace simd; +using namespace simd; - // Holds backslashes and quotes locations. - struct backslash_and_quote { - public: - static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote - copy_and_find(const uint8_t* src, uint8_t* dst); +// 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); - } + 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 + 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 v0(src); - simd8 v1(src + sizeof(v0)); - v0.store(dst); - v1.store(dst + sizeof(v0)); +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 v0(src); + simd8 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(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); - return { - uint32_t(bs_and_quote), // bs_bits - uint32_t(bs_and_quote >> 32) // quote_bits - }; - } + // 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(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); + return { + uint32_t(bs_and_quote), // bs_bits + uint32_t(bs_and_quote >> 32) // quote_bits + }; +} - } // unnamed namespace - } // namespace ppc64 + +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 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 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 @@ -27528,10 +29113,12 @@ namespace simdjson { /* 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_LSX */ -/* amalgamation skipped (editor-only): #include "simdjson/lsx/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 */ @@ -27541,22 +29128,22 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { +namespace ppc64 { - struct open_container; - class dom_parser_implementation; +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 - }; +/** + * 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 ppc64 } // namespace simdjson #endif // SIMDJSON_GENERIC_BASE_H @@ -27573,99 +29160,97 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { - namespace { - namespace jsoncharutils { +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]; - } +// 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]; - } +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; - } +// 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 - } +// 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; - } +// 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 jsoncharutils +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_GENERIC_JSONCHARUTILS_H @@ -27683,69 +29268,69 @@ namespace simdjson { #include namespace simdjson { - namespace ppc64 { - namespace { - /// @private - namespace atomparsing { +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; } +// 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; } - // 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); - } +// 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); +} - 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) { + 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_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) { + 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_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) { + 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; } - } +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; } +} - } // namespace atomparsing - } // unnamed namespace - } // namespace ppc64 +} // namespace atomparsing +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_GENERIC_ATOMPARSING_H @@ -27761,83 +29346,83 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { +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 +// 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"); +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_containers{}; - /** Whether each open container is a [ or { */ - std::unique_ptr 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{}; +class dom_parser_implementation final : public internal::dom_parser_implementation { +public: + /** Tape location of each open { or [ */ + std::unique_ptr open_containers{}; + /** Whether each open container is a [ or { */ + std::unique_ptr 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; + 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); + 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 ppc64 } // namespace simdjson namespace simdjson { - namespace ppc64 { +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; +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) { return CAPACITY; } - // Stage 1 index output - size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; - 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; +// 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) { return CAPACITY; } + // Stage 1 index output + size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; + 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; - } + _capacity = capacity; + return SUCCESS; +} - inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { - // 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; } +inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { + // 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; - } + _max_depth = max_depth; + return SUCCESS; +} - } // namespace ppc64 +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H @@ -27852,129 +29437,148 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { +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 : public internal::implementation_simdjson_result_base { - * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} - * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} - * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} - * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} - * // Your extra methods here - * } - * - * Then any method returning simdjson_result will be chainable with your methods. - */ - template - struct implementation_simdjson_result_base { +// 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 : public internal::implementation_simdjson_result_base { + * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} + * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} + * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} + * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} + * // Your extra methods here + * } + * + * Then any method returning simdjson_result will be chainable with your methods. + */ +template +struct implementation_simdjson_result_base { - /** - * Create a new empty result with error = UNINITIALIZED. - */ - simdjson_inline implementation_simdjson_result_base() noexcept = default; + /** + * 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 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 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; + /** + * 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 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; + /** + * 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_inline error_code error() const 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& value() & noexcept(false); + /** + * 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); - /** - * Take the result value (move it). - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline T&& value() && 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&& take_value() && noexcept(false); + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& 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); + /** + * 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; - 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 + /** + * 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; - } // namespace ppc64 + 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 @@ -27995,8 +29599,8 @@ namespace simdjson { #include namespace simdjson { - namespace ppc64 { - namespace numberparsing { +namespace ppc64 { +namespace numberparsing { #ifdef JSON_TEST_NUMBERS #define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) @@ -28012,540 +29616,547 @@ namespace simdjson { #define BIGINT_NUMBER(SRC) (BIGINT_ERROR) #endif - namespace { +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(negative)) << 63); - std::memcpy(&d, &mantissa, sizeof(d)); - return d; - } +// 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(negative)) << 63); + std::memcpy(&d, &mantissa, sizeof(d)); + return d; +} - // 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 +// 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) + if (0 <= power && power <= 22 && i <= 9007199254740991) #else - if (-22 <= power && power <= 22 && i <= 9007199254740991) + 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 + { + // 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. + // 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; - } + // 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 - // approximatively 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; + // 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 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. - simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]); - // 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. + // 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 - // 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. - simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); - 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); + // 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. - // 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 - } - } + // 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); - mantissa += mantissa & 1; - mantissa >>= 1; + // 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 + } + } - // 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; - } + mantissa += mantissa & 1; + mantissa >>= 1; - // 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(ptr)); - // We do not accept infinite values. + // 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; +} - // 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::max)() || *outDouble < std::numeric_limits::lowest()); - } +// 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(ptr)); + // We do not accept infinite values. - static bool parse_float_fallback(const uint8_t* ptr, const uint8_t* end_ptr, double* outDouble) { - *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(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::max)() || *outDouble < std::numeric_limits::lowest()); +} - // 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::max)() || *outDouble < std::numeric_limits::lowest()); - } +static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) { + *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(end_ptr)); + // We do not accept infinite values. - // 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); - } + // 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::max)() || *outDouble < std::numeric_limits::lowest()); +} - template - 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(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; - } +// 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); +} - simdjson_inline bool is_digit(const uint8_t c) { - return static_cast(c - '0') <= 9; - } +template +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(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 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; +simdjson_inline bool is_digit(const uint8_t c) { + return static_cast(c - '0') <= 9; +} + +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; #ifdef SIMDJSON_SWAR_NUMBER_PARSING #if SIMDJSON_SWAR_NUMBER_PARSING - // this helps if we have lots of decimals! - // this turns out to be frequent enough. - if (is_made_of_eight_digits_fast(p)) { - i = i * 100000000 + parse_eight_digits_unrolled(p); - p += 8; - } + // this helps if we have lots of decimals! + // this turns out to be frequent enough. + if (is_made_of_eight_digits_fast(p)) { + i = i * 100000000 + parse_eight_digits_unrolled(p); + p += 8; + } #endif // SIMDJSON_SWAR_NUMBER_PARSING #endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING - // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) - if (parse_digit(*p, i)) { ++p; } - while (parse_digit(*p, i)) { p++; } - exponent = first_after_period - p; - // Decimal without digits (123.) is illegal - if (exponent == 0) { - return INVALID_NUMBER(src); - } - return SUCCESS; - } + // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) + if (parse_digit(*p, i)) { ++p; } + while (parse_digit(*p, i)) { p++; } + exponent = first_after_period - p; + // Decimal without digits (123.) is illegal + if (exponent == 0) { + return INVALID_NUMBER(src); + } + return SUCCESS; +} - 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 +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. + // 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. + // 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 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; - } + // 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 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); - } +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 +} // unnamed namespace - /** @private */ - static 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 */ +static 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 - 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; - } +/** @private */ +template +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 - simdjson_inline error_code parse_number(const uint8_t* const src, W& writer); +// 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 +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 +// for performance analysis, it is sometimes useful to skip parsing #ifdef SIMDJSON_SKIPNUMBERPARSING - template - simdjson_inline error_code parse_number(const uint8_t* const, W& writer) { - writer.append_s64(0); // always write zero - return SUCCESS; // always succeeds - } +template +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 parse_unsigned(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline bool is_negative(const uint8_t* src) noexcept { return false; } - simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t* src) noexcept { return false; } - simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t* src) noexcept { return number_type::signed_integer; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t * src) noexcept { return number_type::signed_integer; } #else // parse the number at src @@ -28557,744 +30168,753 @@ namespace simdjson { // content and append a space before calling this function. // // Our objective is accurate parsing (ULP of 0) at high speed. - template - 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)) { 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 INVALID_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 { - WRITE_INTEGER(negative ? (~i + 1) : i, src, writer); - } - 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 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; - 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); - // 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 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 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; - 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); - // 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 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; - 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); - // 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 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 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; - while (parse_digit(*src, i)) { src++; } - - // 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 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; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (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 == '.')) { - p++; - const uint8_t* start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (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 = 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; - } - - simdjson_unused simdjson_inline bool is_negative(const uint8_t* src) noexcept { - return (*src == '-'); - } - - simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t* src) noexcept { - bool negative = (*src == '-'); - src += uint8_t(negative); - const uint8_t* p = src; - while (static_cast(*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 get_number_type(const uint8_t* src) noexcept { - bool negative = (*src == '-'); - src += uint8_t(negative); - const uint8_t* p = src; - while (static_cast(*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("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; - } - 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("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 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 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; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (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 == '.')) { - p++; - const uint8_t* start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (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 = 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; - } - - } // unnamed namespace +template +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)) { 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 INVALID_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 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; + 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); + // 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 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 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; + 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); + // 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 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; + 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); + // 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 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 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; + while (parse_digit(*src, i)) { src++; } + + // 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { + return (*src == '-'); +} + +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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 get_number_type(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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("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("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 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +} // unnamed namespace #endif // SIMDJSON_SKIPNUMBERPARSING - } // namespace numberparsing +} // 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; - } +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 ppc64 } // namespace simdjson #endif // SIMDJSON_GENERIC_NUMBERPARSING_H @@ -29311,84 +30931,113 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { +namespace ppc64 { - // - // internal::implementation_simdjson_result_base inline implementation - // +// +// internal::implementation_simdjson_result_base inline implementation +// - template - simdjson_inline void implementation_simdjson_result_base::tie(T& value, error_code& error) && noexcept { - error = this->second; - if (!error) { - value = std::forward>(*this).first; - } - } +template +simdjson_inline void implementation_simdjson_result_base::tie(T &value, error_code &error) && noexcept { + error = this->second; + if (!error) { + value = std::forward>(*this).first; + } +} - template - simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T& value) && noexcept { - error_code error; - std::forward>(*this).tie(value, error); - return error; - } +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T &value) && noexcept { + error_code error; + std::forward>(*this).tie(value, error); + return error; +} - template - simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { - return this->second; - } +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { + return this->second; +} + + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} #if SIMDJSON_EXCEPTIONS - template - simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return this->first; - } +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} - template - simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { - return std::forward>(*this).take_value(); - } +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} - template - simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return std::forward(this->first); - } +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} - template - simdjson_inline implementation_simdjson_result_base::operator T && () && noexcept(false) { - return std::forward>(*this).take_value(); - } + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + +template +simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return this->first; +} + +template +simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { + return std::forward>(*this).take_value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return std::forward(this->first); +} + +template +simdjson_inline implementation_simdjson_result_base::operator T&&() && noexcept(false) { + return std::forward>(*this).take_value(); +} #endif // SIMDJSON_EXCEPTIONS - template - simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { - return this->first; - } +template +simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { + return this->first; +} - template - simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { - return this->first; - } +template +simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { + return this->first; +} - template - simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { - return std::forward(this->first); - } +template +simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { + return std::forward(this->first); +} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T&& value, error_code error) noexcept - : first{ std::forward(value) }, second{ error } {} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept - : implementation_simdjson_result_base(T{}, error) {} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T&& value) noexcept - : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value, error_code error) noexcept + : first{std::forward(value)}, second{error} {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept + : implementation_simdjson_result_base(T{}, error) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value) noexcept + : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} - } // namespace ppc64 +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H @@ -29420,32 +31069,32 @@ namespace simdjson { namespace simdjson { - /** - * Implementation for ALTIVEC (PPC64). - */ - namespace ppc64 { +/** + * 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) {} +/** + * @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& 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; - }; + simdjson_warn_unused error_code create_dom_parser_implementation( + size_t capacity, size_t max_length, + std::unique_ptr &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 ppc64 } // namespace simdjson #endif // SIMDJSON_PPC64_IMPLEMENTATION_H @@ -29465,21 +31114,21 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - /** - * Implementation for ALTIVEC (PPC64). - */ - namespace ppc64 { +/** + * Implementation for ALTIVEC (PPC64). + */ +namespace ppc64 { - class implementation; +class implementation; - namespace { - namespace simd { - template struct simd8; - template struct simd8x64; - } // namespace simd - } // unnamed namespace +namespace { +namespace simd { +template struct simd8; +template struct simd8x64; +} // namespace simd +} // unnamed namespace - } // namespace ppc64 +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_PPC64_BASE_H @@ -29520,73 +31169,73 @@ static_assert(sizeof(__vector unsigned char) <= simdjson::SIMDJSON_PADDING, "ins /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { - namespace { +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) { +// 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; + 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); + 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 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) { +/* 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; + 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); + 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 - } +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); - } +simdjson_inline int count_ones(uint64_t input_num) { + return __builtin_popcountll(input_num); +} #endif - simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, - uint64_t* result) { +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { #if SIMDJSON_REGULAR_VISUAL_STUDIO - * result = value1 + value2; - return *result < value1; + *result = value1 + value2; + return *result < value1; #else - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); #endif - } +} - } // unnamed namespace - } // namespace ppc64 +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_PPC64_BITMANIPULATION_H @@ -29601,41 +31250,41 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { - namespace { +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; - } +// +// 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 +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif @@ -29660,51 +31309,57 @@ namespace simdjson { #endif namespace simdjson { - namespace ppc64 { - namespace numberparsing { +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)); +// 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); + val = bswap_64(val); #elif defined(__FreeBSD__) - val = bswap64(val); + val = bswap64(val); #endif #endif - val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8; - val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16; - return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32); - } + 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; +/** @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; + // 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 + 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); + __uint128_t r = (static_cast<__uint128_t>(value1)) * value2; + answer.low = uint64_t(r); + answer.high = uint64_t(r >> 64); #endif - return answer; - } + return answer; +} - } // namespace numberparsing - } // namespace ppc64 +} // 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 */ @@ -29722,469 +31377,464 @@ namespace simdjson { #include namespace simdjson { - namespace ppc64 { - namespace { - namespace simd { +namespace ppc64 { +namespace { +namespace simd { - using __m128i = __vector unsigned char; +using __m128i = __vector unsigned char; - template struct base { - __m128i value; +template struct base { + __m128i value; - // Zero constructor - simdjson_inline base() : value{ __m128i() } {} + // Zero constructor + simdjson_inline base() : value{__m128i()} {} - // Conversion from SIMD register - simdjson_inline base(const __m128i _value) : value(_value) {} + // 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; } + // 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(this); - *this_cast = *this_cast | other; - return *this_cast; - } - simdjson_inline Child& operator&=(const Child other) { - auto this_cast = static_cast(this); - *this_cast = *this_cast & other; - return *this_cast; - } - simdjson_inline Child& operator^=(const Child other) { - auto this_cast = static_cast(this); - *this_cast = *this_cast ^ other; - return *this_cast; - } - }; + // 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(this); + *this_cast = *this_cast | other; + return *this_cast; + } + simdjson_inline Child &operator&=(const Child other) { + auto this_cast = static_cast(this); + *this_cast = *this_cast & other; + return *this_cast; + } + simdjson_inline Child &operator^=(const Child other) { + auto this_cast = static_cast(this); + *this_cast = *this_cast ^ other; + return *this_cast; + } +}; - template > - struct base8 : base> { - typedef uint16_t bitmask_t; - typedef uint32_t bitmask2_t; +template > +struct base8 : base> { + typedef uint16_t bitmask_t; + typedef uint32_t bitmask2_t; - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m128i _value) : base>(_value) {} + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m128i _value) : base>(_value) {} - friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { - return (__m128i)vec_cmpeq(lhs.value, (__m128i)rhs); - } + friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { + return (__m128i)vec_cmpeq(lhs.value, (__m128i)rhs); + } - static const int SIZE = sizeof(base>::value); + static const int SIZE = sizeof(base>::value); - template - simdjson_inline simd8 prev(simd8 prev_chunk) const { - __m128i chunk = this->value; + template + simdjson_inline simd8 prev(simd8 prev_chunk) const { + __m128i chunk = this->value; #ifdef __LITTLE_ENDIAN__ - chunk = (__m128i)vec_reve(this->value); - prev_chunk = (__m128i)vec_reve((__m128i)prev_chunk); + 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); + chunk = (__m128i)vec_sld((__m128i)prev_chunk, (__m128i)chunk, 16 - N); #ifdef __LITTLE_ENDIAN__ - chunk = (__m128i)vec_reve((__m128i)chunk); + chunk = (__m128i)vec_reve((__m128i)chunk); #endif - return chunk; - } - }; + return chunk; + } +}; - // SIMD byte mask type (returned by things like eq and gt) - template <> struct simd8 : base8 { - static simdjson_inline simd8 splat(bool _value) { - return (__m128i)vec_splats((unsigned char)(-(!!_value))); - } +// SIMD byte mask type (returned by things like eq and gt) +template <> struct simd8 : base8 { + static simdjson_inline simd8 splat(bool _value) { + return (__m128i)vec_splats((unsigned char)(-(!!_value))); + } - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m128i _value) - : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) - : base8(splat(_value)) {} + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m128i _value) + : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) + : base8(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 }; + 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)); + result = ((__vector unsigned long long)vec_vbpermq((__m128i)this->value, + (__m128i)perm_mask)); #ifdef __LITTLE_ENDIAN__ - return static_cast(result[1]); + return static_cast(result[1]); #else - return static_cast(result[0]); + return static_cast(result[0]); #endif - } - simdjson_inline bool any() const { - return !vec_all_eq(this->value, (__m128i)vec_splats(0)); - } - simdjson_inline simd8 operator~() const { - return this->value ^ (__m128i)splat(true); - } - }; + } + simdjson_inline bool any() const { + return !vec_all_eq(this->value, (__m128i)vec_splats(0)); + } + simdjson_inline simd8 operator~() const { + return this->value ^ (__m128i)splat(true); + } +}; - template struct base8_numeric : base8 { - static simdjson_inline simd8 splat(T value) { - (void)value; - return (__m128i)vec_splats(value); - } - static simdjson_inline simd8 zero() { return splat(0); } - static simdjson_inline simd8 load(const T values[16]) { - return (__m128i)(vec_vsx_ld(0, reinterpret_cast(values))); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdjson_inline simd8 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(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, - v14, v15); - } +template struct base8_numeric : base8 { + static simdjson_inline simd8 splat(T value) { + (void)value; + return (__m128i)vec_splats(value); + } + static simdjson_inline simd8 zero() { return splat(0); } + static simdjson_inline simd8 load(const T values[16]) { + return (__m128i)(vec_vsx_ld(0, reinterpret_cast(values))); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, + v14, v15); + } - simdjson_inline base8_numeric() : base8() {} - simdjson_inline base8_numeric(const __m128i _value) - : base8(_value) {} + simdjson_inline base8_numeric() : base8() {} + simdjson_inline base8_numeric(const __m128i _value) + : base8(_value) {} - // Store to array - simdjson_inline void store(T dst[16]) const { - vec_vsx_st(this->value, 0, reinterpret_cast<__m128i*>(dst)); - } + // 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 operator~() const { return *this ^ 0xFFu; } + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - // Addition/subtraction are the same for signed and unsigned - simdjson_inline simd8 operator+(const simd8 other) const { - return (__m128i)((__m128i)this->value + (__m128i)other); - } - simdjson_inline simd8 operator-(const simd8 other) const { - return (__m128i)((__m128i)this->value - (__m128i)other); - } - simdjson_inline simd8& operator+=(const simd8 other) { - *this = *this + other; - return *static_cast *>(this); - } - simdjson_inline simd8& operator-=(const simd8 other) { - *this = *this - other; - return *static_cast *>(this); - } + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { + return (__m128i)((__m128i)this->value + (__m128i)other); + } + simdjson_inline simd8 operator-(const simd8 other) const { + return (__m128i)((__m128i)this->value - (__m128i)other); + } + simdjson_inline simd8 &operator+=(const simd8 other) { + *this = *this + other; + return *static_cast *>(this); + } + simdjson_inline simd8 &operator-=(const simd8 other) { + *this = *this - other; + return *static_cast *>(this); + } - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior - // for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return (__m128i)vec_perm((__m128i)lookup_table, (__m128i)lookup_table, this->value); - } + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior + // for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 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 compress(uint32_t mask) - // would be sensible, but the AVX ISA makes this kind of approach difficult. - template - 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. + // 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 compress(uint32_t mask) + // would be sensible, but the AVX ISA makes this kind of approach difficult. + template + 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] - }; + __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); + __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 }); + // 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(pshufb_combine_table + pop1 * 8)); - __m128i answer = vec_perm(pruned, (__m128i)vec_splats(0), compactmask); - vec_vsx_st(answer, 0, reinterpret_cast<__m128i*>(output)); - } + // 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(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 - simdjson_inline simd8 - 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::repeat_16( - replace0, replace1, replace2, replace3, replace4, replace5, replace6, - replace7, replace8, replace9, replace10, replace11, replace12, - replace13, replace14, replace15)); - } - }; + template + simdjson_inline simd8 + 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::repeat_16( + replace0, replace1, replace2, replace3, replace4, replace5, replace6, + replace7, replace8, replace9, replace10, replace11, replace12, + replace13, replace14, replace15)); + } +}; - // Signed bytes - template <> struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) - : base8_numeric(_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 - 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(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, - v13, v14, v15); - } +// Signed bytes +template <> struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) + : base8_numeric(_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 + 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(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, + v13, v14, v15); + } - // Order-sensitive comparisons - simdjson_inline simd8 - max_val(const simd8 other) const { - return (__m128i)vec_max((__vector signed char)this->value, + // Order-sensitive comparisons + simdjson_inline simd8 + max_val(const simd8 other) const { + return (__m128i)vec_max((__vector signed char)this->value, (__vector signed char)(__m128i)other); - } - simdjson_inline simd8 - min_val(const simd8 other) const { - return (__m128i)vec_min((__vector signed char)this->value, + } + simdjson_inline simd8 + min_val(const simd8 other) const { + return (__m128i)vec_min((__vector signed char)this->value, (__vector signed char)(__m128i)other); - } - simdjson_inline simd8 - operator>(const simd8 other) const { - return (__m128i)vec_cmpgt((__vector signed char)this->value, - (__vector signed char)(__m128i)other); - } - simdjson_inline simd8 - operator<(const simd8 other) const { - return (__m128i)vec_cmplt((__vector signed char)this->value, - (__vector signed char)(__m128i)other); - } - }; + } + simdjson_inline simd8 + operator>(const simd8 other) const { + return (__m128i)vec_cmpgt((__vector signed char)this->value, + (__vector signed char)(__m128i)other); + } + simdjson_inline simd8 + operator<(const simd8 other) const { + return (__m128i)vec_cmplt((__vector signed char)this->value, + (__vector signed char)(__m128i)other); + } +}; - // Unsigned bytes - template <> struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) - : base8_numeric(_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 - 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(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, - v13, v14, v15); - } +// Unsigned bytes +template <> struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) + : base8_numeric(_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 + 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(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, + v13, v14, v15); + } - // Saturated math - simdjson_inline simd8 - saturating_add(const simd8 other) const { - return (__m128i)vec_adds(this->value, (__m128i)other); - } - simdjson_inline simd8 - saturating_sub(const simd8 other) const { - return (__m128i)vec_subs(this->value, (__m128i)other); - } + // Saturated math + simdjson_inline simd8 + saturating_add(const simd8 other) const { + return (__m128i)vec_adds(this->value, (__m128i)other); + } + simdjson_inline simd8 + saturating_sub(const simd8 other) const { + return (__m128i)vec_subs(this->value, (__m128i)other); + } - // Order-specific operations - simdjson_inline simd8 - max_val(const simd8 other) const { - return (__m128i)vec_max(this->value, (__m128i)other); - } - simdjson_inline simd8 - min_val(const simd8 other) const { - return (__m128i)vec_min(this->value, (__m128i)other); - } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 - gt_bits(const simd8 other) const { - return this->saturating_sub(other); - } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 - lt_bits(const simd8 other) const { - return other.saturating_sub(*this); - } - simdjson_inline simd8 - operator<=(const simd8 other) const { - return other.max_val(*this) == other; - } - simdjson_inline simd8 - operator>=(const simd8 other) const { - return other.min_val(*this) == other; - } - simdjson_inline simd8 - operator>(const simd8 other) const { - return this->gt_bits(other).any_bits_set(); - } - simdjson_inline simd8 - operator<(const simd8 other) const { - return this->gt_bits(other).any_bits_set(); - } + // Order-specific operations + simdjson_inline simd8 + max_val(const simd8 other) const { + return (__m128i)vec_max(this->value, (__m128i)other); + } + simdjson_inline simd8 + min_val(const simd8 other) const { + return (__m128i)vec_min(this->value, (__m128i)other); + } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 + gt_bits(const simd8 other) const { + return this->saturating_sub(other); + } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 + lt_bits(const simd8 other) const { + return other.saturating_sub(*this); + } + simdjson_inline simd8 + operator<=(const simd8 other) const { + return other.max_val(*this) == other; + } + simdjson_inline simd8 + operator>=(const simd8 other) const { + return other.min_val(*this) == other; + } + simdjson_inline simd8 + operator>(const simd8 other) const { + return this->gt_bits(other).any_bits_set(); + } + simdjson_inline simd8 + operator<(const simd8 other) const { + return this->gt_bits(other).any_bits_set(); + } - // Bit-specific operations - simdjson_inline simd8 bits_not_set() const { - return (__m128i)vec_cmpeq(this->value, (__m128i)vec_splats(uint8_t(0))); - } - simdjson_inline simd8 bits_not_set(simd8 bits) const { - return (*this & bits).bits_not_set(); - } - simdjson_inline simd8 any_bits_set() const { - return ~this->bits_not_set(); - } - simdjson_inline simd8 any_bits_set(simd8 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 bits) const { - return vec_all_eq(vec_and(this->value, (__m128i)bits), - (__m128i)vec_splats(0)); - } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { - return !bits_not_set_anywhere(bits); - } - template simdjson_inline simd8 shr() const { - return simd8( - (__m128i)vec_sr(this->value, (__m128i)vec_splat_u8(N))); - } - template simdjson_inline simd8 shl() const { - return simd8( - (__m128i)vec_sl(this->value, (__m128i)vec_splat_u8(N))); - } - }; + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { + return (__m128i)vec_cmpeq(this->value, (__m128i)vec_splats(uint8_t(0))); + } + simdjson_inline simd8 bits_not_set(simd8 bits) const { + return (*this & bits).bits_not_set(); + } + simdjson_inline simd8 any_bits_set() const { + return ~this->bits_not_set(); + } + simdjson_inline simd8 any_bits_set(simd8 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 bits) const { + return vec_all_eq(vec_and(this->value, (__m128i)bits), + (__m128i)vec_splats(0)); + } + simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { + return !bits_not_set_anywhere(bits); + } + template simdjson_inline simd8 shr() const { + return simd8( + (__m128i)vec_sr(this->value, (__m128i)vec_splat_u8(N))); + } + template simdjson_inline simd8 shl() const { + return simd8( + (__m128i)vec_sl(this->value, (__m128i)vec_splat_u8(N))); + } +}; - template struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 4, - "PPC64 kernel should use four registers per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; +template struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 4, + "PPC64 kernel should use four registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& - operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed + simd8x64(const simd8x64 &o) = delete; // no copy allowed + simd8x64 & + operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, - const simd8 chunk2, const simd8 chunk3) - : chunks{ chunk0, chunk1, chunk2, chunk3 } {} - simdjson_inline simd8x64(const T ptr[64]) - : chunks{ simd8::load(ptr), simd8::load(ptr + 16), - simd8::load(ptr + 32), simd8::load(ptr + 48) } {} + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, + const simd8 chunk2, const simd8 chunk3) + : chunks{chunk0, chunk1, chunk2, chunk3} {} + simdjson_inline simd8x64(const T ptr[64]) + : chunks{simd8::load(ptr), simd8::load(ptr + 16), + simd8::load(ptr + 32), simd8::load(ptr + 48)} {} - simdjson_inline void store(T ptr[64]) const { - this->chunks[0].store(ptr + sizeof(simd8) * 0); - this->chunks[1].store(ptr + sizeof(simd8) * 1); - this->chunks[2].store(ptr + sizeof(simd8) * 2); - this->chunks[3].store(ptr + sizeof(simd8) * 3); - } + simdjson_inline void store(T ptr[64]) const { + this->chunks[0].store(ptr + sizeof(simd8) * 0); + this->chunks[1].store(ptr + sizeof(simd8) * 1); + this->chunks[2].store(ptr + sizeof(simd8) * 2); + this->chunks[3].store(ptr + sizeof(simd8) * 3); + } - simdjson_inline simd8 reduce_or() const { - return (this->chunks[0] | this->chunks[1]) | - (this->chunks[2] | this->chunks[3]); - } + simdjson_inline simd8 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 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 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 mask = simd8::splat(m); - return simd8x64(this->chunks[0] == mask, this->chunks[1] == mask, - this->chunks[2] == mask, this->chunks[3] == mask) - .to_bitmask(); - } + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64(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& other) const { - return simd8x64(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 eq(const simd8x64 &other) const { + return simd8x64(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 mask = simd8::splat(m); - return simd8x64(this->chunks[0] <= mask, this->chunks[1] <= mask, - this->chunks[2] <= mask, this->chunks[3] <= mask) - .to_bitmask(); - } - }; // struct simd8x64 + simdjson_inline uint64_t lteq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64(this->chunks[0] <= mask, this->chunks[1] <= mask, + this->chunks[2] <= mask, this->chunks[3] <= mask) + .to_bitmask(); + } +}; // struct simd8x64 - } // namespace simd - } // unnamed namespace - } // namespace ppc64 +} // namespace simd +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_PPC64_SIMD_INPUT_H @@ -30201,58 +31851,84 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { - namespace { +namespace ppc64 { +namespace { - using namespace simd; +using namespace simd; - // Holds backslashes and quotes locations. - struct backslash_and_quote { - public: - static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote - copy_and_find(const uint8_t* src, uint8_t* dst); +// 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); - } + 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 + 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 v0(src); - simd8 v1(src + sizeof(v0)); - v0.store(dst); - v1.store(dst + sizeof(v0)); +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 v0(src); + simd8 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(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); - return { - uint32_t(bs_and_quote), // bs_bits - uint32_t(bs_and_quote >> 32) // quote_bits - }; - } + // 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(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); + return { + uint32_t(bs_and_quote), // bs_bits + uint32_t(bs_and_quote >> 32) // quote_bits + }; +} - } // unnamed namespace - } // namespace ppc64 + +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 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 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 @@ -30277,13 +31953,13 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { - namespace { +namespace ppc64 { +namespace { - struct json_character_block; +struct json_character_block; - } // unnamed namespace - } // namespace ppc64 +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_BASE_H @@ -30300,14 +31976,14 @@ namespace simdjson { // Interface a dom parser implementation must fulfill namespace simdjson { - namespace ppc64 { - namespace { +namespace ppc64 { +namespace { - simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3); - simdjson_inline bool is_ascii(const simd8x64& input); +simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3); +simdjson_inline bool is_ascii(const simd8x64& input); - } // unnamed namespace - } // namespace ppc64 +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_DOM_PARSER_IMPLEMENTATION_H @@ -30322,22 +31998,22 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { - namespace { +namespace ppc64 { +namespace { - struct json_character_block { - static simdjson_inline json_character_block classify(const simd::simd8x64& in); +struct json_character_block { + static simdjson_inline json_character_block classify(const simd::simd8x64& in); - simdjson_inline uint64_t whitespace() const noexcept { return _whitespace; } - simdjson_inline uint64_t op() const noexcept { return _op; } - simdjson_inline uint64_t scalar() const noexcept { return ~(op() | whitespace()); } + simdjson_inline uint64_t whitespace() const noexcept { return _whitespace; } + simdjson_inline uint64_t op() const noexcept { return _op; } + simdjson_inline uint64_t scalar() const noexcept { return ~(op() | whitespace()); } - uint64_t _whitespace; - uint64_t _op; - }; + uint64_t _whitespace; + uint64_t _op; +}; - } // unnamed namespace - } // namespace ppc64 +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_JSON_CHARACTER_BLOCK_H @@ -30356,30 +32032,30 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { - namespace { - namespace stage1 { +namespace ppc64 { +namespace { +namespace stage1 { - class bit_indexer; - template - struct buf_block_reader; - struct json_block; - class json_minifier; - class json_scanner; - struct json_string_block; - class json_string_scanner; - class json_structural_indexer; +class bit_indexer; +template +struct buf_block_reader; +struct json_block; +class json_minifier; +class json_scanner; +struct json_string_block; +class json_string_scanner; +class json_structural_indexer; - } // namespace stage1 +} // namespace stage1 - namespace utf8_validation { - struct utf8_checker; - } // namespace utf8_validation +namespace utf8_validation { +struct utf8_checker; +} // namespace utf8_validation - using utf8_validation::utf8_checker; +using utf8_validation::utf8_checker; - } // unnamed namespace - } // namespace ppc64 +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_BASE_H @@ -30396,109 +32072,109 @@ namespace simdjson { #include namespace simdjson { - namespace ppc64 { - namespace { - namespace stage1 { +namespace ppc64 { +namespace { +namespace stage1 { - // Walks through a buffer in block-sized increments, loading the last part with spaces - template - struct buf_block_reader { - public: - simdjson_inline buf_block_reader(const uint8_t* _buf, size_t _len); - simdjson_inline size_t block_index(); - simdjson_inline bool has_full_block() const; - simdjson_inline const uint8_t* full_block() const; - /** - * Get the last block, padded with spaces. - * - * There will always be a last block, with at least 1 byte, unless len == 0 (in which case this - * function fills the buffer with spaces and returns 0. In particular, if len == STEP_SIZE there - * will be 0 full_blocks and 1 remainder block with STEP_SIZE bytes and no spaces for padding. - * - * @return the number of effective characters in the last block. - */ - simdjson_inline size_t get_remainder(uint8_t* dst) const; - simdjson_inline void advance(); - private: - const uint8_t* buf; - const size_t len; - const size_t lenminusstep; - size_t idx; - }; +// Walks through a buffer in block-sized increments, loading the last part with spaces +template +struct buf_block_reader { +public: + simdjson_inline buf_block_reader(const uint8_t *_buf, size_t _len); + simdjson_inline size_t block_index(); + simdjson_inline bool has_full_block() const; + simdjson_inline const uint8_t *full_block() const; + /** + * Get the last block, padded with spaces. + * + * There will always be a last block, with at least 1 byte, unless len == 0 (in which case this + * function fills the buffer with spaces and returns 0. In particular, if len == STEP_SIZE there + * will be 0 full_blocks and 1 remainder block with STEP_SIZE bytes and no spaces for padding. + * + * @return the number of effective characters in the last block. + */ + simdjson_inline size_t get_remainder(uint8_t *dst) const; + simdjson_inline void advance(); +private: + const uint8_t *buf; + const size_t len; + const size_t lenminusstep; + size_t idx; +}; - // Routines to print masks and text for debugging bitmask operations - simdjson_unused static char* format_input_text_64(const uint8_t* text) { - static char buf[sizeof(simd8x64) + 1]; - for (size_t i = 0; i < sizeof(simd8x64); i++) { - buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]); - } - buf[sizeof(simd8x64)] = '\0'; - return buf; - } +// Routines to print masks and text for debugging bitmask operations +simdjson_unused static char * format_input_text_64(const uint8_t *text) { + static char buf[sizeof(simd8x64) + 1]; + for (size_t i=0; i); i++) { + buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]); + } + buf[sizeof(simd8x64)] = '\0'; + return buf; +} - // Routines to print masks and text for debugging bitmask operations - simdjson_unused static char* format_input_text(const simd8x64& in) { - static char buf[sizeof(simd8x64) + 1]; - in.store(reinterpret_cast(buf)); - for (size_t i = 0; i < sizeof(simd8x64); i++) { - if (buf[i] < ' ') { buf[i] = '_'; } - } - buf[sizeof(simd8x64)] = '\0'; - return buf; - } +// Routines to print masks and text for debugging bitmask operations +simdjson_unused static char * format_input_text(const simd8x64& in) { + static char buf[sizeof(simd8x64) + 1]; + in.store(reinterpret_cast(buf)); + for (size_t i=0; i); i++) { + if (buf[i] < ' ') { buf[i] = '_'; } + } + buf[sizeof(simd8x64)] = '\0'; + return buf; +} - simdjson_unused static char* format_input_text(const simd8x64& in, uint64_t mask) { - static char buf[sizeof(simd8x64) + 1]; - in.store(reinterpret_cast(buf)); - for (size_t i = 0; i < sizeof(simd8x64); i++) { - if (buf[i] <= ' ') { buf[i] = '_'; } - if (!(mask & (size_t(1) << i))) { buf[i] = ' '; } - } - buf[sizeof(simd8x64)] = '\0'; - return buf; - } +simdjson_unused static char * format_input_text(const simd8x64& in, uint64_t mask) { + static char buf[sizeof(simd8x64) + 1]; + in.store(reinterpret_cast(buf)); + for (size_t i=0; i); i++) { + if (buf[i] <= ' ') { buf[i] = '_'; } + if (!(mask & (size_t(1) << i))) { buf[i] = ' '; } + } + buf[sizeof(simd8x64)] = '\0'; + return buf; +} - simdjson_unused static char* format_mask(uint64_t mask) { - static char buf[sizeof(simd8x64) + 1]; - for (size_t i = 0; i < 64; i++) { - buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' '; - } - buf[64] = '\0'; - return buf; - } +simdjson_unused static char * format_mask(uint64_t mask) { + static char buf[sizeof(simd8x64) + 1]; + for (size_t i=0; i<64; i++) { + buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' '; + } + buf[64] = '\0'; + return buf; +} - template - simdjson_inline buf_block_reader::buf_block_reader(const uint8_t* _buf, size_t _len) : buf{ _buf }, len{ _len }, lenminusstep{ len < STEP_SIZE ? 0 : len - STEP_SIZE }, idx{ 0 } {} +template +simdjson_inline buf_block_reader::buf_block_reader(const uint8_t *_buf, size_t _len) : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE}, idx{0} {} - template - simdjson_inline size_t buf_block_reader::block_index() { return idx; } +template +simdjson_inline size_t buf_block_reader::block_index() { return idx; } - template - simdjson_inline bool buf_block_reader::has_full_block() const { - return idx < lenminusstep; - } +template +simdjson_inline bool buf_block_reader::has_full_block() const { + return idx < lenminusstep; +} - template - simdjson_inline const uint8_t* buf_block_reader::full_block() const { - return &buf[idx]; - } +template +simdjson_inline const uint8_t *buf_block_reader::full_block() const { + return &buf[idx]; +} - template - simdjson_inline size_t buf_block_reader::get_remainder(uint8_t* dst) const { - if (len == idx) { return 0; } // memcpy(dst, null, 0) will trigger an error with some sanitizers - std::memset(dst, 0x20, STEP_SIZE); // std::memset STEP_SIZE because it's more efficient to write out 8 or 16 bytes at once. - std::memcpy(dst, buf + idx, len - idx); - return len - idx; - } +template +simdjson_inline size_t buf_block_reader::get_remainder(uint8_t *dst) const { + if(len == idx) { return 0; } // memcpy(dst, null, 0) will trigger an error with some sanitizers + std::memset(dst, 0x20, STEP_SIZE); // std::memset STEP_SIZE because it's more efficient to write out 8 or 16 bytes at once. + std::memcpy(dst, buf + idx, len - idx); + return len - idx; +} - template - simdjson_inline void buf_block_reader::advance() { - idx += STEP_SIZE; - } +template +simdjson_inline void buf_block_reader::advance() { + idx += STEP_SIZE; +} - } // namespace stage1 - } // unnamed namespace - } // namespace ppc64 +} // namespace stage1 +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_BUF_BLOCK_READER_H @@ -30514,145 +32190,145 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { - namespace { - namespace stage1 { +namespace ppc64 { +namespace { +namespace stage1 { - /** - * Scans for escape characters in JSON, taking care with multiple backslashes (\\n vs. \n). - */ - struct json_escape_scanner { - /** The actual escape characters (the backslashes themselves). */ - uint64_t next_is_escaped = 0ULL; +/** + * Scans for escape characters in JSON, taking care with multiple backslashes (\\n vs. \n). + */ +struct json_escape_scanner { + /** The actual escape characters (the backslashes themselves). */ + uint64_t next_is_escaped = 0ULL; - struct escaped_and_escape { - /** - * Mask of escaped characters. - * - * ``` - * \n \\n \\\n \\\\n \ - * 0100100010100101000 - * n \ \ n \ \ - * ``` - */ - uint64_t escaped; - /** - * Mask of escape characters. - * - * ``` - * \n \\n \\\n \\\\n \ - * 1001000101001010001 - * \ \ \ \ \ \ \ - * ``` - */ - uint64_t escape; - }; + struct escaped_and_escape { + /** + * Mask of escaped characters. + * + * ``` + * \n \\n \\\n \\\\n \ + * 0100100010100101000 + * n \ \ n \ \ + * ``` + */ + uint64_t escaped; + /** + * Mask of escape characters. + * + * ``` + * \n \\n \\\n \\\\n \ + * 1001000101001010001 + * \ \ \ \ \ \ \ + * ``` + */ + uint64_t escape; + }; - /** - * Get a mask of both escape and escaped characters (the characters following a backslash). - * - * @param potential_escape A mask of the character that can escape others (but could be - * escaped itself). e.g. block.eq('\\') - */ - simdjson_really_inline escaped_and_escape next(uint64_t backslash) noexcept { + /** + * Get a mask of both escape and escaped characters (the characters following a backslash). + * + * @param potential_escape A mask of the character that can escape others (but could be + * escaped itself). e.g. block.eq('\\') + */ + simdjson_really_inline escaped_and_escape next(uint64_t backslash) noexcept { #if !SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT - if (!backslash) { return { next_escaped_without_backslashes(), 0 }; } + if (!backslash) { return {next_escaped_without_backslashes(), 0}; } #endif - // | | Mask (shows characters instead of 1's) | Depth | Instructions | - // |--------------------------------|----------------------------------------|-------|---------------------| - // | string | `\\n_\\\n___\\\n___\\\\___\\\\__\\\` | | | - // | | ` even odd even odd odd` | | | - // | potential_escape | ` \ \\\ \\\ \\\\ \\\\ \\\` | 1 | 1 (backslash & ~first_is_escaped) - // | escape_and_terminal_code | ` \n \ \n \ \n \ \ \ \ \ \` | 5 | 5 (next_escape_and_terminal_code()) - // | escaped | `\ \ n \ n \ \ \ \ \ ` X | 6 | 7 (escape_and_terminal_code ^ (potential_escape | first_is_escaped)) - // | escape | ` \ \ \ \ \ \ \ \ \ \` | 6 | 8 (escape_and_terminal_code & backslash) - // | first_is_escaped | `\ ` | 7 (*) | 9 (escape >> 63) () - // (*) this is not needed until the next iteration - uint64_t escape_and_terminal_code = next_escape_and_terminal_code(backslash & ~this->next_is_escaped); - uint64_t escaped = escape_and_terminal_code ^ (backslash | this->next_is_escaped); - uint64_t escape = escape_and_terminal_code & backslash; - this->next_is_escaped = escape >> 63; - return { escaped, escape }; - } + // | | Mask (shows characters instead of 1's) | Depth | Instructions | + // |--------------------------------|----------------------------------------|-------|---------------------| + // | string | `\\n_\\\n___\\\n___\\\\___\\\\__\\\` | | | + // | | ` even odd even odd odd` | | | + // | potential_escape | ` \ \\\ \\\ \\\\ \\\\ \\\` | 1 | 1 (backslash & ~first_is_escaped) + // | escape_and_terminal_code | ` \n \ \n \ \n \ \ \ \ \ \` | 5 | 5 (next_escape_and_terminal_code()) + // | escaped | `\ \ n \ n \ \ \ \ \ ` X | 6 | 7 (escape_and_terminal_code ^ (potential_escape | first_is_escaped)) + // | escape | ` \ \ \ \ \ \ \ \ \ \` | 6 | 8 (escape_and_terminal_code & backslash) + // | first_is_escaped | `\ ` | 7 (*) | 9 (escape >> 63) () + // (*) this is not needed until the next iteration + uint64_t escape_and_terminal_code = next_escape_and_terminal_code(backslash & ~this->next_is_escaped); + uint64_t escaped = escape_and_terminal_code ^ (backslash | this->next_is_escaped); + uint64_t escape = escape_and_terminal_code & backslash; + this->next_is_escaped = escape >> 63; + return {escaped, escape}; + } - private: - static constexpr const uint64_t ODD_BITS = 0xAAAAAAAAAAAAAAAAULL; +private: + static constexpr const uint64_t ODD_BITS = 0xAAAAAAAAAAAAAAAAULL; - simdjson_really_inline uint64_t next_escaped_without_backslashes() noexcept { - uint64_t escaped = this->next_is_escaped; - this->next_is_escaped = 0; - return escaped; - } + simdjson_really_inline uint64_t next_escaped_without_backslashes() noexcept { + uint64_t escaped = this->next_is_escaped; + this->next_is_escaped = 0; + return escaped; + } - /** - * Returns a mask of the next escape characters (masking out escaped backslashes), along with - * any non-backslash escape codes. - * - * \n \\n \\\n \\\\n returns: - * \n \ \ \n \ \ - * 11 100 1011 10100 - * - * You are expected to mask out the first bit yourself if the previous block had a trailing - * escape. - * - * & the result with potential_escape to get just the escape characters. - * ^ the result with (potential_escape | first_is_escaped) to get escaped characters. - */ - static simdjson_really_inline uint64_t next_escape_and_terminal_code(uint64_t potential_escape) noexcept { - // If we were to just shift and mask out any odd bits, we'd actually get a *half* right answer: - // any even-aligned backslash runs would be correct! Odd-aligned backslash runs would be - // inverted (\\\ would be 010 instead of 101). - // - // ``` - // string: | ____\\\\_\\\\_____ | - // maybe_escaped | ODD | \ \ \ \ | - // even-aligned ^^^ ^^^^ odd-aligned - // ``` - // - // Taking that into account, our basic strategy is: - // - // 1. Use subtraction to produce a mask with 1's for even-aligned runs and 0's for - // odd-aligned runs. - // 2. XOR all odd bits, which masks out the odd bits in even-aligned runs, and brings IN the - // odd bits in odd-aligned runs. - // 3. & with backslash to clean up any stray bits. - // runs are set to 0, and then XORing with "odd": - // - // | | Mask (shows characters instead of 1's) | Instructions | - // |--------------------------------|----------------------------------------|---------------------| - // | string | `\\n_\\\n___\\\n___\\\\___\\\\__\\\` | - // | | ` even odd even odd odd` | - // | maybe_escaped | ` n \\n \\n \\\_ \\\_ \\` X | 1 (potential_escape << 1) - // | maybe_escaped_and_odd | ` \n_ \\n _ \\\n_ _ \\\__ _\\\_ \\\` | 1 (maybe_escaped | odd) - // | even_series_codes_and_odd | ` n_\\\ _ n_ _\\\\ _ _ ` | 1 (maybe_escaped_and_odd - potential_escape) - // | escape_and_terminal_code | ` \n \ \n \ \n \ \ \ \ \ \` | 1 (^ odd) - // + /** + * Returns a mask of the next escape characters (masking out escaped backslashes), along with + * any non-backslash escape codes. + * + * \n \\n \\\n \\\\n returns: + * \n \ \ \n \ \ + * 11 100 1011 10100 + * + * You are expected to mask out the first bit yourself if the previous block had a trailing + * escape. + * + * & the result with potential_escape to get just the escape characters. + * ^ the result with (potential_escape | first_is_escaped) to get escaped characters. + */ + static simdjson_really_inline uint64_t next_escape_and_terminal_code(uint64_t potential_escape) noexcept { + // If we were to just shift and mask out any odd bits, we'd actually get a *half* right answer: + // any even-aligned backslash runs would be correct! Odd-aligned backslash runs would be + // inverted (\\\ would be 010 instead of 101). + // + // ``` + // string: | ____\\\\_\\\\_____ | + // maybe_escaped | ODD | \ \ \ \ | + // even-aligned ^^^ ^^^^ odd-aligned + // ``` + // + // Taking that into account, our basic strategy is: + // + // 1. Use subtraction to produce a mask with 1's for even-aligned runs and 0's for + // odd-aligned runs. + // 2. XOR all odd bits, which masks out the odd bits in even-aligned runs, and brings IN the + // odd bits in odd-aligned runs. + // 3. & with backslash to clean up any stray bits. + // runs are set to 0, and then XORing with "odd": + // + // | | Mask (shows characters instead of 1's) | Instructions | + // |--------------------------------|----------------------------------------|---------------------| + // | string | `\\n_\\\n___\\\n___\\\\___\\\\__\\\` | + // | | ` even odd even odd odd` | + // | maybe_escaped | ` n \\n \\n \\\_ \\\_ \\` X | 1 (potential_escape << 1) + // | maybe_escaped_and_odd | ` \n_ \\n _ \\\n_ _ \\\__ _\\\_ \\\` | 1 (maybe_escaped | odd) + // | even_series_codes_and_odd | ` n_\\\ _ n_ _\\\\ _ _ ` | 1 (maybe_escaped_and_odd - potential_escape) + // | escape_and_terminal_code | ` \n \ \n \ \n \ \ \ \ \ \` | 1 (^ odd) + // - // Escaped characters are characters following an escape. - uint64_t maybe_escaped = potential_escape << 1; + // Escaped characters are characters following an escape. + uint64_t maybe_escaped = potential_escape << 1; - // To distinguish odd from even escape sequences, therefore, we turn on any *starting* - // escapes that are on an odd byte. (We actually bring in all odd bits, for speed.) - // - Odd runs of backslashes are 0000, and the code at the end ("n" in \n or \\n) is 1. - // - Odd runs of backslashes are 1111, and the code at the end ("n" in \n or \\n) is 0. - // - All other odd bytes are 1, and even bytes are 0. - uint64_t maybe_escaped_and_odd_bits = maybe_escaped | ODD_BITS; - uint64_t even_series_codes_and_odd_bits = maybe_escaped_and_odd_bits - potential_escape; + // To distinguish odd from even escape sequences, therefore, we turn on any *starting* + // escapes that are on an odd byte. (We actually bring in all odd bits, for speed.) + // - Odd runs of backslashes are 0000, and the code at the end ("n" in \n or \\n) is 1. + // - Odd runs of backslashes are 1111, and the code at the end ("n" in \n or \\n) is 0. + // - All other odd bytes are 1, and even bytes are 0. + uint64_t maybe_escaped_and_odd_bits = maybe_escaped | ODD_BITS; + uint64_t even_series_codes_and_odd_bits = maybe_escaped_and_odd_bits - potential_escape; - // Now we flip all odd bytes back with xor. This: - // - Makes odd runs of backslashes go from 0000 to 1010 - // - Makes even runs of backslashes go from 1111 to 1010 - // - Sets actually-escaped codes to 1 (the n in \n and \\n: \n = 11, \\n = 100) - // - Resets all other bytes to 0 - return even_series_codes_and_odd_bits ^ ODD_BITS; - } - }; + // Now we flip all odd bytes back with xor. This: + // - Makes odd runs of backslashes go from 0000 to 1010 + // - Makes even runs of backslashes go from 1111 to 1010 + // - Sets actually-escaped codes to 1 (the n in \n and \\n: \n = 11, \\n = 100) + // - Resets all other bytes to 0 + return even_series_codes_and_odd_bits ^ ODD_BITS; + } +}; - } // namespace stage1 - } // unnamed namespace - } // namespace ppc64 +} // namespace stage1 +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRING_SCANNER_H @@ -30668,93 +32344,93 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { - namespace { - namespace stage1 { +namespace ppc64 { +namespace { +namespace stage1 { - struct json_string_block { - // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 - simdjson_really_inline json_string_block(uint64_t escaped, uint64_t quote, uint64_t in_string) : - _escaped(escaped), _quote(quote), _in_string(in_string) {} +struct json_string_block { + // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 + simdjson_really_inline json_string_block(uint64_t escaped, uint64_t quote, uint64_t in_string) : + _escaped(escaped), _quote(quote), _in_string(in_string) {} - // Escaped characters (characters following an escape() character) - simdjson_really_inline uint64_t escaped() const { return _escaped; } - // Real (non-backslashed) quotes - simdjson_really_inline uint64_t quote() const { return _quote; } - // Only characters inside the string (not including the quotes) - simdjson_really_inline uint64_t string_content() const { return _in_string & ~_quote; } - // Return a mask of whether the given characters are inside a string (only works on non-quotes) - simdjson_really_inline uint64_t non_quote_inside_string(uint64_t mask) const { return mask & _in_string; } - // Return a mask of whether the given characters are inside a string (only works on non-quotes) - simdjson_really_inline uint64_t non_quote_outside_string(uint64_t mask) const { return mask & ~_in_string; } - // Tail of string (everything except the start quote) - simdjson_really_inline uint64_t string_tail() const { return _in_string ^ _quote; } + // Escaped characters (characters following an escape() character) + simdjson_really_inline uint64_t escaped() const { return _escaped; } + // Real (non-backslashed) quotes + simdjson_really_inline uint64_t quote() const { return _quote; } + // Only characters inside the string (not including the quotes) + simdjson_really_inline uint64_t string_content() const { return _in_string & ~_quote; } + // Return a mask of whether the given characters are inside a string (only works on non-quotes) + simdjson_really_inline uint64_t non_quote_inside_string(uint64_t mask) const { return mask & _in_string; } + // Return a mask of whether the given characters are inside a string (only works on non-quotes) + simdjson_really_inline uint64_t non_quote_outside_string(uint64_t mask) const { return mask & ~_in_string; } + // Tail of string (everything except the start quote) + simdjson_really_inline uint64_t string_tail() const { return _in_string ^ _quote; } - // escaped characters (backslashed--does not include the hex characters after \u) - uint64_t _escaped; - // real quotes (non-escaped ones) - uint64_t _quote; - // string characters (includes start quote but not end quote) - uint64_t _in_string; - }; + // escaped characters (backslashed--does not include the hex characters after \u) + uint64_t _escaped; + // real quotes (non-escaped ones) + uint64_t _quote; + // string characters (includes start quote but not end quote) + uint64_t _in_string; +}; - // Scans blocks for string characters, storing the state necessary to do so - class json_string_scanner { - public: - simdjson_really_inline json_string_block next(const simd::simd8x64& in); - // Returns either UNCLOSED_STRING or SUCCESS - simdjson_really_inline error_code finish(); +// Scans blocks for string characters, storing the state necessary to do so +class json_string_scanner { +public: + simdjson_really_inline json_string_block next(const simd::simd8x64& in); + // Returns either UNCLOSED_STRING or SUCCESS + simdjson_really_inline error_code finish(); - private: - // Scans for escape characters - json_escape_scanner escape_scanner{}; - // Whether the last iteration was still inside a string (all 1's = true, all 0's = false). - uint64_t prev_in_string = 0ULL; - }; +private: + // Scans for escape characters + json_escape_scanner escape_scanner{}; + // Whether the last iteration was still inside a string (all 1's = true, all 0's = false). + uint64_t prev_in_string = 0ULL; +}; - // - // Return a mask of all string characters plus end quotes. - // - // prev_escaped is overflow saying whether the next character is escaped. - // prev_in_string is overflow saying whether we're still in a string. - // - // Backslash sequences outside of quotes will be detected in stage 2. - // - simdjson_really_inline json_string_block json_string_scanner::next(const simd::simd8x64& in) { - const uint64_t backslash = in.eq('\\'); - const uint64_t escaped = escape_scanner.next(backslash).escaped; - const uint64_t quote = in.eq('"') & ~escaped; +// +// Return a mask of all string characters plus end quotes. +// +// prev_escaped is overflow saying whether the next character is escaped. +// prev_in_string is overflow saying whether we're still in a string. +// +// Backslash sequences outside of quotes will be detected in stage 2. +// +simdjson_really_inline json_string_block json_string_scanner::next(const simd::simd8x64& in) { + const uint64_t backslash = in.eq('\\'); + const uint64_t escaped = escape_scanner.next(backslash).escaped; + const uint64_t quote = in.eq('"') & ~escaped; - // - // prefix_xor flips on bits inside the string (and flips off the end quote). - // - // Then we xor with prev_in_string: if we were in a string already, its effect is flipped - // (characters inside strings are outside, and characters outside strings are inside). - // - const uint64_t in_string = prefix_xor(quote) ^ prev_in_string; + // + // prefix_xor flips on bits inside the string (and flips off the end quote). + // + // Then we xor with prev_in_string: if we were in a string already, its effect is flipped + // (characters inside strings are outside, and characters outside strings are inside). + // + const uint64_t in_string = prefix_xor(quote) ^ prev_in_string; - // - // Check if we're still in a string at the end of the box so the next block will know - // - prev_in_string = uint64_t(static_cast(in_string) >> 63); + // + // Check if we're still in a string at the end of the box so the next block will know + // + prev_in_string = uint64_t(static_cast(in_string) >> 63); - // Use ^ to turn the beginning quote off, and the end quote on. + // Use ^ to turn the beginning quote off, and the end quote on. - // We are returning a function-local object so either we get a move constructor - // or we get copy elision. - return json_string_block(escaped, quote, in_string); - } + // We are returning a function-local object so either we get a move constructor + // or we get copy elision. + return json_string_block(escaped, quote, in_string); +} - simdjson_really_inline error_code json_string_scanner::finish() { - if (prev_in_string) { - return UNCLOSED_STRING; - } - return SUCCESS; - } +simdjson_really_inline error_code json_string_scanner::finish() { + if (prev_in_string) { + return UNCLOSED_STRING; + } + return SUCCESS; +} - } // namespace stage1 - } // unnamed namespace - } // namespace ppc64 +} // namespace stage1 +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRING_SCANNER_H @@ -30770,206 +32446,203 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { - namespace { - namespace utf8_validation { +namespace ppc64 { +namespace { +namespace utf8_validation { - using namespace simd; +using namespace simd; - simdjson_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { - // Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) - // Bit 1 = Too Long (ASCII followed by continuation) - // Bit 2 = Overlong 3-byte - // Bit 4 = Surrogate - // Bit 5 = Overlong 2-byte - // Bit 7 = Two Continuations - constexpr const uint8_t TOO_SHORT = 1 << 0; // 11______ 0_______ - // 11______ 11______ - constexpr const uint8_t TOO_LONG = 1 << 1; // 0_______ 10______ - constexpr const uint8_t OVERLONG_3 = 1 << 2; // 11100000 100_____ - constexpr const uint8_t SURROGATE = 1 << 4; // 11101101 101_____ - constexpr const uint8_t OVERLONG_2 = 1 << 5; // 1100000_ 10______ - constexpr const uint8_t TWO_CONTS = 1 << 7; // 10______ 10______ - constexpr const uint8_t TOO_LARGE = 1 << 3; // 11110100 1001____ - // 11110100 101_____ - // 11110101 1001____ - // 11110101 101_____ - // 1111011_ 1001____ - // 1111011_ 101_____ - // 11111___ 1001____ - // 11111___ 101_____ - constexpr const uint8_t TOO_LARGE_1000 = 1 << 6; - // 11110101 1000____ - // 1111011_ 1000____ - // 11111___ 1000____ - constexpr const uint8_t OVERLONG_4 = 1 << 6; // 11110000 1000____ + simdjson_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { +// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) +// Bit 1 = Too Long (ASCII followed by continuation) +// Bit 2 = Overlong 3-byte +// Bit 4 = Surrogate +// Bit 5 = Overlong 2-byte +// Bit 7 = Two Continuations + constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______ + // 11______ 11______ + constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______ + constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____ + constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____ + constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______ + constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______ + constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____ + // 11110100 101_____ + // 11110101 1001____ + // 11110101 101_____ + // 1111011_ 1001____ + // 1111011_ 101_____ + // 11111___ 1001____ + // 11111___ 101_____ + constexpr const uint8_t TOO_LARGE_1000 = 1<<6; + // 11110101 1000____ + // 1111011_ 1000____ + // 11111___ 1000____ + constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____ - const simd8 byte_1_high = prev1.shr<4>().lookup_16( - // 0_______ ________ - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - // 10______ ________ - TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, - // 1100____ ________ - TOO_SHORT | OVERLONG_2, - // 1101____ ________ - TOO_SHORT, - // 1110____ ________ - TOO_SHORT | OVERLONG_3 | SURROGATE, - // 1111____ ________ - TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 - ); - constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . - const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( - // ____0000 ________ - CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, - // ____0001 ________ - CARRY | OVERLONG_2, - // ____001_ ________ - CARRY, - CARRY, + const simd8 byte_1_high = prev1.shr<4>().lookup_16( + // 0_______ ________ + TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, + TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, + // 10______ ________ + TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, + // 1100____ ________ + TOO_SHORT | OVERLONG_2, + // 1101____ ________ + TOO_SHORT, + // 1110____ ________ + TOO_SHORT | OVERLONG_3 | SURROGATE, + // 1111____ ________ + TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 + ); + constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . + const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( + // ____0000 ________ + CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, + // ____0001 ________ + CARRY | OVERLONG_2, + // ____001_ ________ + CARRY, + CARRY, - // ____0100 ________ - CARRY | TOO_LARGE, - // ____0101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____011_ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, + // ____0100 ________ + CARRY | TOO_LARGE, + // ____0101 ________ + CARRY | TOO_LARGE | TOO_LARGE_1000, + // ____011_ ________ + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____1___ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____1101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000 - ); - const simd8 byte_2_high = input.shr<4>().lookup_16( - // ________ 0_______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, + // ____1___ ________ + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + // ____1101 ________ + CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000 + ); + const simd8 byte_2_high = input.shr<4>().lookup_16( + // ________ 0_______ + TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, + TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - // ________ 1000____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, - // ________ 1001____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, - // ________ 101_____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, + // ________ 1000____ + TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, + // ________ 1001____ + TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, + // ________ 101_____ + TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, + TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - // ________ 11______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT - ); - return (byte_1_high & byte_1_low & byte_2_high); - } - simdjson_inline simd8 check_multibyte_lengths(const simd8 input, - const simd8 prev_input, const simd8 sc) { - simd8 prev2 = input.prev<2>(prev_input); - simd8 prev3 = input.prev<3>(prev_input); - simd8 must23 = must_be_2_3_continuation(prev2, prev3); - simd8 must23_80 = must23 & uint8_t(0x80); - return must23_80 ^ sc; - } + // ________ 11______ + TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT + ); + return (byte_1_high & byte_1_low & byte_2_high); + } + simdjson_inline simd8 check_multibyte_lengths(const simd8 input, + const simd8 prev_input, const simd8 sc) { + simd8 prev2 = input.prev<2>(prev_input); + simd8 prev3 = input.prev<3>(prev_input); + simd8 must23 = must_be_2_3_continuation(prev2, prev3); + simd8 must23_80 = must23 & uint8_t(0x80); + return must23_80 ^ sc; + } - // - // Return nonzero if there are incomplete multibyte characters at the end of the block: - // e.g. if there is a 4-byte character, but it's 3 bytes from the end. - // - simdjson_inline simd8 is_incomplete(const simd8 input) { - // If the previous input's last 3 bytes match this, they're too short (they ended at EOF): - // ... 1111____ 111_____ 11______ -#if SIMDJSON_IMPLEMENTATION_ICELAKE - static const uint8_t max_array[64] = { - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0xf0u - 1, 0xe0u - 1, 0xc0u - 1 - }; + // + // Return nonzero if there are incomplete multibyte characters at the end of the block: + // e.g. if there is a 4-byte character, but it's 3 bytes from the end. + // + simdjson_inline simd8 is_incomplete(const simd8 input) { + // If the previous input's last 3 bytes match this, they're too short (they ended at EOF): + // ... 1111____ 111_____ 11______ +#if SIMDJSON_IMPLEMENTATION_ICELAKE || (SIMDJSON_IMPLEMENTATION_RVV_VLS && __riscv_v_fixed_vlen >= 512) + static const uint8_t max_array[64] = { + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0xf0u-1, 0xe0u-1, 0xc0u-1 + }; #else - static const uint8_t max_array[32] = { - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0xf0u - 1, 0xe0u - 1, 0xc0u - 1 - }; + static const uint8_t max_array[32] = { + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0xf0u-1, 0xe0u-1, 0xc0u-1 + }; #endif - const simd8 max_value(&max_array[sizeof(max_array) - sizeof(simd8)]); - return input.gt_bits(max_value); - } + const simd8 max_value(&max_array[sizeof(max_array)-sizeof(simd8)]); + return input.gt_bits(max_value); + } - struct utf8_checker { - // If this is nonzero, there has been a UTF-8 error. - simd8 error; - // The last input we received - simd8 prev_input_block; - // Whether the last input we received was incomplete (used for ASCII fast path) - simd8 prev_incomplete; + struct utf8_checker { + // If this is nonzero, there has been a UTF-8 error. + simd8 error; + // The last input we received + simd8 prev_input_block; + // Whether the last input we received was incomplete (used for ASCII fast path) + simd8 prev_incomplete; - // - // Check whether the current bytes are valid UTF-8. - // - simdjson_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { - // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes - // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) - simd8 prev1 = input.prev<1>(prev_input); - simd8 sc = check_special_cases(input, prev1); - this->error |= check_multibyte_lengths(input, prev_input, sc); - } + // + // Check whether the current bytes are valid UTF-8. + // + simdjson_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { + // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes + // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) + simd8 prev1 = input.prev<1>(prev_input); + simd8 sc = check_special_cases(input, prev1); + this->error |= check_multibyte_lengths(input, prev_input, sc); + } - // The only problem that can happen at EOF is that a multibyte character is too short - // or a byte value too large in the last bytes: check_special_cases only checks for bytes - // too large in the first of two bytes. - simdjson_inline void check_eof() { - // If the previous block had incomplete UTF-8 characters at the end, an ASCII block can't - // possibly finish them. - this->error |= this->prev_incomplete; - } + // The only problem that can happen at EOF is that a multibyte character is too short + // or a byte value too large in the last bytes: check_special_cases only checks for bytes + // too large in the first of two bytes. + simdjson_inline void check_eof() { + // If the previous block had incomplete UTF-8 characters at the end, an ASCII block can't + // possibly finish them. + this->error |= this->prev_incomplete; + } - simdjson_inline void check_next_input(const simd8x64& input) { - if (simdjson_likely(is_ascii(input))) { - this->error |= this->prev_incomplete; - } - else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 1) - || (simd8x64::NUM_CHUNKS == 2) - || (simd8x64::NUM_CHUNKS == 4), - "We support one, two or four chunks per 64-byte block."); - SIMDJSON_IF_CONSTEXPR(simd8x64::NUM_CHUNKS == 1) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - } -else SIMDJSON_IF_CONSTEXPR(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); + simdjson_inline void check_next_input(const simd8x64& input) { + if(simdjson_likely(is_ascii(input))) { + this->error |= this->prev_incomplete; + } else { + // you might think that a for-loop would work, but under Visual Studio, it is not good enough. + static_assert((simd8x64::NUM_CHUNKS == 1) + ||(simd8x64::NUM_CHUNKS == 2) + || (simd8x64::NUM_CHUNKS == 4), + "We support one, two or four chunks per 64-byte block."); + SIMDJSON_IF_CONSTEXPR (simd8x64::NUM_CHUNKS == 1) { + this->check_utf8_bytes(input.get<0>(), this->prev_input_block); + } else SIMDJSON_IF_CONSTEXPR (simd8x64::NUM_CHUNKS == 2) { + this->check_utf8_bytes(input.get<0>(), this->prev_input_block); + this->check_utf8_bytes(input.get<1>(), input.get<0>()); + } else SIMDJSON_IF_CONSTEXPR (simd8x64::NUM_CHUNKS == 4) { + this->check_utf8_bytes(input.get<0>(), this->prev_input_block); + this->check_utf8_bytes(input.get<1>(), input.get<0>()); + this->check_utf8_bytes(input.get<2>(), input.get<1>()); + this->check_utf8_bytes(input.get<3>(), input.get<2>()); } - else SIMDJSON_IF_CONSTEXPR(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - this->prev_incomplete = is_incomplete(input.chunks[simd8x64::NUM_CHUNKS - 1]); - this->prev_input_block = input.chunks[simd8x64::NUM_CHUNKS - 1]; - } - } - // do not forget to call check_eof! - simdjson_inline error_code errors() { - return this->error.any_bits_set_anywhere() ? error_code::UTF8_ERROR : error_code::SUCCESS; - } + this->prev_incomplete = is_incomplete(input.get::NUM_CHUNKS-1>()); + this->prev_input_block = input.get::NUM_CHUNKS-1>(); + } + } + // do not forget to call check_eof! + simdjson_warn_unused simdjson_inline error_code errors() { + return this->error.any_bits_set_anywhere() ? error_code::UTF8_ERROR : error_code::SUCCESS; + } - }; // struct utf8_checker - } // namespace utf8_validation + }; // struct utf8_checker +} // namespace utf8_validation - } // unnamed namespace - } // namespace ppc64 +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_UTF8_LOOKUP4_ALGORITHM_H @@ -30986,161 +32659,161 @@ else SIMDJSON_IF_CONSTEXPR(simd8x64::NUM_CHUNKS == 2) { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { - namespace { - namespace stage1 { +namespace ppc64 { +namespace { +namespace stage1 { - /** - * A block of scanned json, with information on operators and scalars. - * - * We seek to identify pseudo-structural characters. Anything that is inside - * a string must be omitted (hence & ~_string.string_tail()). - * Otherwise, pseudo-structural characters come in two forms. - * 1. We have the structural characters ([,],{,},:, comma). The - * term 'structural character' is from the JSON RFC. - * 2. We have the 'scalar pseudo-structural characters'. - * Scalars are quotes, and any character except structural characters and white space. - * - * To identify the scalar pseudo-structural characters, we must look at what comes - * before them: it must be a space, a quote or a structural characters. - * Starting with simdjson v0.3, we identify them by - * negation: we identify everything that is followed by a non-quote scalar, - * and we negate that. Whatever remains must be a 'scalar pseudo-structural character'. - */ - struct json_block { - public: - // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 - simdjson_inline json_block(json_string_block&& string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : - _string(std::move(string)), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} - simdjson_inline json_block(json_string_block string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : - _string(string), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} +/** + * A block of scanned json, with information on operators and scalars. + * + * We seek to identify pseudo-structural characters. Anything that is inside + * a string must be omitted (hence & ~_string.string_tail()). + * Otherwise, pseudo-structural characters come in two forms. + * 1. We have the structural characters ([,],{,},:, comma). The + * term 'structural character' is from the JSON RFC. + * 2. We have the 'scalar pseudo-structural characters'. + * Scalars are quotes, and any character except structural characters and white space. + * + * To identify the scalar pseudo-structural characters, we must look at what comes + * before them: it must be a space, a quote or a structural characters. + * Starting with simdjson v0.3, we identify them by + * negation: we identify everything that is followed by a non-quote scalar, + * and we negate that. Whatever remains must be a 'scalar pseudo-structural character'. + */ +struct json_block { +public: + // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 + simdjson_inline json_block(json_string_block&& string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : + _string(std::move(string)), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} + simdjson_inline json_block(json_string_block string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : + _string(string), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} - /** - * The start of structurals. - * In simdjson prior to v0.3, these were called the pseudo-structural characters. - **/ - simdjson_inline uint64_t structural_start() const noexcept { return potential_structural_start() & ~_string.string_tail(); } - /** All JSON whitespace (i.e. not in a string) */ - simdjson_inline uint64_t whitespace() const noexcept { return non_quote_outside_string(_characters.whitespace()); } + /** + * The start of structurals. + * In simdjson prior to v0.3, these were called the pseudo-structural characters. + **/ + simdjson_inline uint64_t structural_start() const noexcept { return potential_structural_start() & ~_string.string_tail(); } + /** All JSON whitespace (i.e. not in a string) */ + simdjson_inline uint64_t whitespace() const noexcept { return non_quote_outside_string(_characters.whitespace()); } - // Helpers + // Helpers - /** Whether the given characters are inside a string (only works on non-quotes) */ - simdjson_inline uint64_t non_quote_inside_string(uint64_t mask) const noexcept { return _string.non_quote_inside_string(mask); } - /** Whether the given characters are outside a string (only works on non-quotes) */ - simdjson_inline uint64_t non_quote_outside_string(uint64_t mask) const noexcept { return _string.non_quote_outside_string(mask); } + /** Whether the given characters are inside a string (only works on non-quotes) */ + simdjson_inline uint64_t non_quote_inside_string(uint64_t mask) const noexcept { return _string.non_quote_inside_string(mask); } + /** Whether the given characters are outside a string (only works on non-quotes) */ + simdjson_inline uint64_t non_quote_outside_string(uint64_t mask) const noexcept { return _string.non_quote_outside_string(mask); } - // string and escape characters - json_string_block _string; - // whitespace, structural characters ('operators'), scalars - json_character_block _characters; - // whether the previous character was a scalar - uint64_t _follows_potential_nonquote_scalar; - private: - // Potential structurals (i.e. disregarding strings) + // string and escape characters + json_string_block _string; + // whitespace, structural characters ('operators'), scalars + json_character_block _characters; + // whether the previous character was a scalar + uint64_t _follows_potential_nonquote_scalar; +private: + // Potential structurals (i.e. disregarding strings) - /** - * structural elements ([,],{,},:, comma) plus scalar starts like 123, true and "abc". - * They may reside inside a string. - **/ - simdjson_inline uint64_t potential_structural_start() const noexcept { return _characters.op() | potential_scalar_start(); } - /** - * The start of non-operator runs, like 123, true and "abc". - * It main reside inside a string. - **/ - simdjson_inline uint64_t potential_scalar_start() const noexcept { - // The term "scalar" refers to anything except structural characters and white space - // (so letters, numbers, quotes). - // Whenever it is preceded by something that is not a structural element ({,},[,],:, ") nor a white-space - // then we know that it is irrelevant structurally. - return _characters.scalar() & ~follows_potential_scalar(); - } - /** - * Whether the given character is immediately after a non-operator like 123, true. - * The characters following a quote are not included. - */ - simdjson_inline uint64_t follows_potential_scalar() const noexcept { - // _follows_potential_nonquote_scalar: is defined as marking any character that follows a character - // that is not a structural element ({,},[,],:, comma) nor a quote (") and that is not a - // white space. - // It is understood that within quoted region, anything at all could be marked (irrelevant). - return _follows_potential_nonquote_scalar; - } - }; + /** + * structural elements ([,],{,},:, comma) plus scalar starts like 123, true and "abc". + * They may reside inside a string. + **/ + simdjson_inline uint64_t potential_structural_start() const noexcept { return _characters.op() | potential_scalar_start(); } + /** + * The start of non-operator runs, like 123, true and "abc". + * It main reside inside a string. + **/ + simdjson_inline uint64_t potential_scalar_start() const noexcept { + // The term "scalar" refers to anything except structural characters and white space + // (so letters, numbers, quotes). + // Whenever it is preceded by something that is not a structural element ({,},[,],:, ") nor a white-space + // then we know that it is irrelevant structurally. + return _characters.scalar() & ~follows_potential_scalar(); + } + /** + * Whether the given character is immediately after a non-operator like 123, true. + * The characters following a quote are not included. + */ + simdjson_inline uint64_t follows_potential_scalar() const noexcept { + // _follows_potential_nonquote_scalar: is defined as marking any character that follows a character + // that is not a structural element ({,},[,],:, comma) nor a quote (") and that is not a + // white space. + // It is understood that within quoted region, anything at all could be marked (irrelevant). + return _follows_potential_nonquote_scalar; + } +}; - /** - * Scans JSON for important bits: structural characters or 'operators', strings, and scalars. - * - * The scanner starts by calculating two distinct things: - * - string characters (taking \" into account) - * - structural characters or 'operators' ([]{},:, comma) - * and scalars (runs of non-operators like 123, true and "abc") - * - * To minimize data dependency (a key component of the scanner's speed), it finds these in parallel: - * in particular, the operator/scalar bit will find plenty of things that are actually part of - * strings. When we're done, json_block will fuse the two together by masking out tokens that are - * part of a string. - */ - class json_scanner { - public: - json_scanner() = default; - simdjson_inline json_block next(const simd::simd8x64& in); - // Returns either UNCLOSED_STRING or SUCCESS - simdjson_inline error_code finish(); +/** + * Scans JSON for important bits: structural characters or 'operators', strings, and scalars. + * + * The scanner starts by calculating two distinct things: + * - string characters (taking \" into account) + * - structural characters or 'operators' ([]{},:, comma) + * and scalars (runs of non-operators like 123, true and "abc") + * + * To minimize data dependency (a key component of the scanner's speed), it finds these in parallel: + * in particular, the operator/scalar bit will find plenty of things that are actually part of + * strings. When we're done, json_block will fuse the two together by masking out tokens that are + * part of a string. + */ +class json_scanner { +public: + json_scanner() = default; + simdjson_inline json_block next(const simd::simd8x64& in); + // Returns either UNCLOSED_STRING or SUCCESS + simdjson_warn_unused simdjson_inline error_code finish(); - private: - // Whether the last character of the previous iteration is part of a scalar token - // (anything except whitespace or a structural character/'operator'). - uint64_t prev_scalar = 0ULL; - json_string_scanner string_scanner{}; - }; +private: + // Whether the last character of the previous iteration is part of a scalar token + // (anything except whitespace or a structural character/'operator'). + uint64_t prev_scalar = 0ULL; + json_string_scanner string_scanner{}; +}; - // - // Check if the current character immediately follows a matching character. - // - // For example, this checks for quotes with backslashes in front of them: - // - // const uint64_t backslashed_quote = in.eq('"') & immediately_follows(in.eq('\'), prev_backslash); - // - simdjson_inline uint64_t follows(const uint64_t match, uint64_t& overflow) { - const uint64_t result = match << 1 | overflow; - overflow = match >> 63; - return result; - } +// +// Check if the current character immediately follows a matching character. +// +// For example, this checks for quotes with backslashes in front of them: +// +// const uint64_t backslashed_quote = in.eq('"') & immediately_follows(in.eq('\'), prev_backslash); +// +simdjson_inline uint64_t follows(const uint64_t match, uint64_t &overflow) { + const uint64_t result = match << 1 | overflow; + overflow = match >> 63; + return result; +} - simdjson_inline json_block json_scanner::next(const simd::simd8x64& in) { - json_string_block strings = string_scanner.next(in); - // identifies the white-space and the structural characters - json_character_block characters = json_character_block::classify(in); - // The term "scalar" refers to anything except structural characters and white space - // (so letters, numbers, quotes). - // We want follows_scalar to mark anything that follows a non-quote scalar (so letters and numbers). - // - // A terminal quote should either be followed by a structural character (comma, brace, bracket, colon) - // or nothing. However, we still want ' "a string"true ' to mark the 't' of 'true' as a potential - // pseudo-structural character just like we would if we had ' "a string" true '; otherwise we - // may need to add an extra check when parsing strings. - // - // Performance: there are many ways to skin this cat. - const uint64_t nonquote_scalar = characters.scalar() & ~strings.quote(); - uint64_t follows_nonquote_scalar = follows(nonquote_scalar, prev_scalar); - // We are returning a function-local object so either we get a move constructor - // or we get copy elision. - return json_block( - strings,// strings is a function-local object so either it moves or the copy is elided. - characters, - follows_nonquote_scalar - ); - } +simdjson_inline json_block json_scanner::next(const simd::simd8x64& in) { + json_string_block strings = string_scanner.next(in); + // identifies the white-space and the structural characters + json_character_block characters = json_character_block::classify(in); + // The term "scalar" refers to anything except structural characters and white space + // (so letters, numbers, quotes). + // We want follows_scalar to mark anything that follows a non-quote scalar (so letters and numbers). + // + // A terminal quote should either be followed by a structural character (comma, brace, bracket, colon) + // or nothing. However, we still want ' "a string"true ' to mark the 't' of 'true' as a potential + // pseudo-structural character just like we would if we had ' "a string" true '; otherwise we + // may need to add an extra check when parsing strings. + // + // Performance: there are many ways to skin this cat. + const uint64_t nonquote_scalar = characters.scalar() & ~strings.quote(); + uint64_t follows_nonquote_scalar = follows(nonquote_scalar, prev_scalar); + // We are returning a function-local object so either we get a move constructor + // or we get copy elision. + return json_block( + strings,// strings is a function-local object so either it moves or the copy is elided. + characters, + follows_nonquote_scalar + ); +} - simdjson_inline error_code json_scanner::finish() { - return string_scanner.finish(); - } +simdjson_warn_unused simdjson_inline error_code json_scanner::finish() { + return string_scanner.finish(); +} - } // namespace stage1 - } // unnamed namespace - } // namespace ppc64 +} // namespace stage1 +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_SCANNER_H @@ -31158,99 +32831,99 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { - namespace { - namespace stage1 { +namespace ppc64 { +namespace { +namespace stage1 { - /** - * This algorithm is used to quickly identify the last structural position that - * makes up a complete document. - * - * It does this by going backwards and finding the last *document boundary* (a - * place where one value follows another without a comma between them). If the - * last document (the characters after the boundary) has an equal number of - * start and end brackets, it is considered complete. - * - * Simply put, we iterate over the structural characters, starting from - * the end. We consider that we found the end of a JSON document when the - * first element of the pair is NOT one of these characters: '{' '[' ':' ',' - * and when the second element is NOT one of these characters: '}' ']' ':' ','. - * - * This simple comparison works most of the time, but it does not cover cases - * where the batch's structural indexes contain a perfect amount of documents. - * In such a case, we do not have access to the structural index which follows - * the last document, therefore, we do not have access to the second element in - * the pair, and that means we cannot identify the last document. To fix this - * issue, we keep a count of the open and closed curly/square braces we found - * while searching for the pair. When we find a pair AND the count of open and - * closed curly/square braces is the same, we know that we just passed a - * complete document, therefore the last json buffer location is the end of the - * batch. - */ - simdjson_inline uint32_t find_next_document_index(dom_parser_implementation& parser) { - // Variant: do not count separately, just figure out depth - if (parser.n_structural_indexes == 0) { return 0; } - auto arr_cnt = 0; - auto obj_cnt = 0; - for (auto i = parser.n_structural_indexes - 1; i > 0; i--) { - auto idxb = parser.structural_indexes[i]; - switch (parser.buf[idxb]) { - case ':': - case ',': - continue; - case '}': - obj_cnt--; - continue; - case ']': - arr_cnt--; - continue; - case '{': - obj_cnt++; - break; - case '[': - arr_cnt++; - break; - } - auto idxa = parser.structural_indexes[i - 1]; - switch (parser.buf[idxa]) { - case '{': - case '[': - case ':': - case ',': - continue; - } - // Last document is complete, so the next document will appear after! - if (!arr_cnt && !obj_cnt) { - return parser.n_structural_indexes; - } - // Last document is incomplete; mark the document at i + 1 as the next one - return i; - } - // If we made it to the end, we want to finish counting to see if we have a full document. - switch (parser.buf[parser.structural_indexes[0]]) { - case '}': - obj_cnt--; - break; - case ']': - arr_cnt--; - break; - case '{': - obj_cnt++; - break; - case '[': - arr_cnt++; - break; - } - if (!arr_cnt && !obj_cnt) { - // We have a complete document. - return parser.n_structural_indexes; - } - return 0; - } +/** + * This algorithm is used to quickly identify the last structural position that + * makes up a complete document. + * + * It does this by going backwards and finding the last *document boundary* (a + * place where one value follows another without a comma between them). If the + * last document (the characters after the boundary) has an equal number of + * start and end brackets, it is considered complete. + * + * Simply put, we iterate over the structural characters, starting from + * the end. We consider that we found the end of a JSON document when the + * first element of the pair is NOT one of these characters: '{' '[' ':' ',' + * and when the second element is NOT one of these characters: '}' ']' ':' ','. + * + * This simple comparison works most of the time, but it does not cover cases + * where the batch's structural indexes contain a perfect amount of documents. + * In such a case, we do not have access to the structural index which follows + * the last document, therefore, we do not have access to the second element in + * the pair, and that means we cannot identify the last document. To fix this + * issue, we keep a count of the open and closed curly/square braces we found + * while searching for the pair. When we find a pair AND the count of open and + * closed curly/square braces is the same, we know that we just passed a + * complete document, therefore the last json buffer location is the end of the + * batch. + */ +simdjson_inline uint32_t find_next_document_index(dom_parser_implementation &parser) { + // Variant: do not count separately, just figure out depth + if(parser.n_structural_indexes == 0) { return 0; } + auto arr_cnt = 0; + auto obj_cnt = 0; + for (auto i = parser.n_structural_indexes - 1; i > 0; i--) { + auto idxb = parser.structural_indexes[i]; + switch (parser.buf[idxb]) { + case ':': + case ',': + continue; + case '}': + obj_cnt--; + continue; + case ']': + arr_cnt--; + continue; + case '{': + obj_cnt++; + break; + case '[': + arr_cnt++; + break; + } + auto idxa = parser.structural_indexes[i - 1]; + switch (parser.buf[idxa]) { + case '{': + case '[': + case ':': + case ',': + continue; + } + // Last document is complete, so the next document will appear after! + if (!arr_cnt && !obj_cnt) { + return parser.n_structural_indexes; + } + // Last document is incomplete; mark the document at i + 1 as the next one + return i; + } + // If we made it to the end, we want to finish counting to see if we have a full document. + switch (parser.buf[parser.structural_indexes[0]]) { + case '}': + obj_cnt--; + break; + case ']': + arr_cnt--; + break; + case '{': + obj_cnt++; + break; + case '[': + arr_cnt++; + break; + } + if (!arr_cnt && !obj_cnt) { + // We have a complete document. + return parser.n_structural_indexes; + } + return 0; +} - } // namespace stage1 - } // unnamed namespace - } // namespace ppc64 +} // namespace stage1 +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_FIND_NEXT_DOCUMENT_INDEX_H @@ -31272,92 +32945,92 @@ namespace simdjson { // "simdjson/stage1.h" (this simplifies amalgation) namespace simdjson { - namespace ppc64 { - namespace { - namespace stage1 { +namespace ppc64 { +namespace { +namespace stage1 { - class json_minifier { - public: - template - static error_code minify(const uint8_t* buf, size_t len, uint8_t* dst, size_t& dst_len) noexcept; +class json_minifier { +public: + template + static error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) noexcept; - private: - simdjson_inline json_minifier(uint8_t* _dst) - : dst{ _dst } - {} - template - simdjson_inline void step(const uint8_t* block_buf, buf_block_reader& reader) noexcept; - simdjson_inline void next(const simd::simd8x64& in, const json_block& block); - simdjson_inline error_code finish(uint8_t* dst_start, size_t& dst_len); - json_scanner scanner{}; - uint8_t* dst; - }; +private: + simdjson_inline json_minifier(uint8_t *_dst) + : dst{_dst} + {} + template + simdjson_inline void step(const uint8_t *block_buf, buf_block_reader &reader) noexcept; + simdjson_inline void next(const simd::simd8x64& in, const json_block& block); + simdjson_warn_unused simdjson_inline error_code finish(uint8_t *dst_start, size_t &dst_len); + json_scanner scanner{}; + uint8_t *dst; +}; - simdjson_inline void json_minifier::next(const simd::simd8x64& in, const json_block& block) { - uint64_t mask = block.whitespace(); - dst += in.compress(mask, dst); - } +simdjson_inline void json_minifier::next(const simd::simd8x64& in, const json_block& block) { + uint64_t mask = block.whitespace(); + dst += in.compress(mask, dst); +} - simdjson_inline error_code json_minifier::finish(uint8_t* dst_start, size_t& dst_len) { - error_code error = scanner.finish(); - if (error) { dst_len = 0; return error; } - dst_len = dst - dst_start; - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code json_minifier::finish(uint8_t *dst_start, size_t &dst_len) { + error_code error = scanner.finish(); + if (error) { dst_len = 0; return error; } + dst_len = dst - dst_start; + return SUCCESS; +} - template<> - simdjson_inline void json_minifier::step<128>(const uint8_t* block_buf, buf_block_reader<128>& reader) noexcept { - simd::simd8x64 in_1(block_buf); - simd::simd8x64 in_2(block_buf + 64); - json_block block_1 = scanner.next(in_1); - json_block block_2 = scanner.next(in_2); - this->next(in_1, block_1); - this->next(in_2, block_2); - reader.advance(); - } +template<> +simdjson_inline void json_minifier::step<128>(const uint8_t *block_buf, buf_block_reader<128> &reader) noexcept { + simd::simd8x64 in_1(block_buf); + simd::simd8x64 in_2(block_buf+64); + json_block block_1 = scanner.next(in_1); + json_block block_2 = scanner.next(in_2); + this->next(in_1, block_1); + this->next(in_2, block_2); + reader.advance(); +} - template<> - simdjson_inline void json_minifier::step<64>(const uint8_t* block_buf, buf_block_reader<64>& reader) noexcept { - simd::simd8x64 in_1(block_buf); - json_block block_1 = scanner.next(in_1); - this->next(block_buf, block_1); - reader.advance(); - } +template<> +simdjson_inline void json_minifier::step<64>(const uint8_t *block_buf, buf_block_reader<64> &reader) noexcept { + simd::simd8x64 in_1(block_buf); + json_block block_1 = scanner.next(in_1); + this->next(block_buf, block_1); + reader.advance(); +} - template - error_code json_minifier::minify(const uint8_t* buf, size_t len, uint8_t* dst, size_t& dst_len) noexcept { - buf_block_reader reader(buf, len); - json_minifier minifier(dst); +template +error_code json_minifier::minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) noexcept { + buf_block_reader reader(buf, len); + json_minifier minifier(dst); - // Index the first n-1 blocks - while (reader.has_full_block()) { - minifier.step(reader.full_block(), reader); - } + // Index the first n-1 blocks + while (reader.has_full_block()) { + minifier.step(reader.full_block(), reader); + } - // Index the last (remainder) block, padded with spaces - uint8_t block[STEP_SIZE]; - size_t remaining_bytes = reader.get_remainder(block); - if (remaining_bytes > 0) { - // We do not want to write directly to the output stream. Rather, we write - // to a local buffer (for safety). - uint8_t out_block[STEP_SIZE]; - uint8_t* const guarded_dst{ minifier.dst }; - minifier.dst = out_block; - minifier.step(block, reader); - size_t to_write = minifier.dst - out_block; - // In some cases, we could be enticed to consider the padded spaces - // as part of the string. This is fine as long as we do not write more - // than we consumed. - if (to_write > remaining_bytes) { to_write = remaining_bytes; } - memcpy(guarded_dst, out_block, to_write); - minifier.dst = guarded_dst + to_write; - } - return minifier.finish(dst, dst_len); - } + // Index the last (remainder) block, padded with spaces + uint8_t block[STEP_SIZE]; + size_t remaining_bytes = reader.get_remainder(block); + if (remaining_bytes > 0) { + // We do not want to write directly to the output stream. Rather, we write + // to a local buffer (for safety). + uint8_t out_block[STEP_SIZE]; + uint8_t * const guarded_dst{minifier.dst}; + minifier.dst = out_block; + minifier.step(block, reader); + size_t to_write = minifier.dst - out_block; + // In some cases, we could be enticed to consider the padded spaces + // as part of the string. This is fine as long as we do not write more + // than we consumed. + if(to_write > remaining_bytes) { to_write = remaining_bytes; } + memcpy(guarded_dst, out_block, to_write); + minifier.dst = guarded_dst + to_write; + } + return minifier.finish(dst, dst_len); +} - } // namespace stage1 - } // unnamed namespace - } // namespace ppc64 +} // namespace stage1 +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_MINIFIER_H @@ -31383,341 +33056,339 @@ namespace simdjson { // "simdjson/stage1.h" (this simplifies amalgation) namespace simdjson { - namespace ppc64 { - namespace { - namespace stage1 { +namespace ppc64 { +namespace { +namespace stage1 { - class bit_indexer { - public: - uint32_t* tail; +class bit_indexer { +public: + uint32_t *tail; - simdjson_inline bit_indexer(uint32_t* index_buf) : tail(index_buf) {} + simdjson_inline bit_indexer(uint32_t *index_buf) : tail(index_buf) {} #if SIMDJSON_PREFER_REVERSE_BITS - /** - * ARM lacks a fast trailing zero instruction, but it has a fast - * bit reversal instruction and a fast leading zero instruction. - * Thus it may be profitable to reverse the bits (once) and then - * to rely on a sequence of instructions that call the leading - * zero instruction. - * - * Performance notes: - * The chosen routine is not optimal in terms of data dependency - * since zero_leading_bit might require two instructions. However, - * it tends to minimize the total number of instructions which is - * beneficial. - */ - simdjson_inline void write_index(uint32_t idx, uint64_t& rev_bits, int i) { - int lz = leading_zeroes(rev_bits); - this->tail[i] = static_cast(idx) + lz; - rev_bits = zero_leading_bit(rev_bits, lz); - } + /** + * ARM lacks a fast trailing zero instruction, but it has a fast + * bit reversal instruction and a fast leading zero instruction. + * Thus it may be profitable to reverse the bits (once) and then + * to rely on a sequence of instructions that call the leading + * zero instruction. + * + * Performance notes: + * The chosen routine is not optimal in terms of data dependency + * since zero_leading_bit might require two instructions. However, + * it tends to minimize the total number of instructions which is + * beneficial. + */ + simdjson_inline void write_index(uint32_t idx, uint64_t& rev_bits, int i) { + int lz = leading_zeroes(rev_bits); + this->tail[i] = static_cast(idx) + lz; + rev_bits = zero_leading_bit(rev_bits, lz); + } #else - /** - * Under recent x64 systems, we often have both a fast trailing zero - * instruction and a fast 'clear-lower-bit' instruction so the following - * algorithm can be competitive. - */ + /** + * Under recent x64 systems, we often have both a fast trailing zero + * instruction and a fast 'clear-lower-bit' instruction so the following + * algorithm can be competitive. + */ - simdjson_inline void write_index(uint32_t idx, uint64_t& bits, int i) { - this->tail[i] = idx + trailing_zeroes(bits); - bits = clear_lowest_bit(bits); - } + simdjson_inline void write_index(uint32_t idx, uint64_t& bits, int i) { + this->tail[i] = idx + trailing_zeroes(bits); + bits = clear_lowest_bit(bits); + } #endif // SIMDJSON_PREFER_REVERSE_BITS - template - simdjson_inline int write_indexes(uint32_t idx, uint64_t& bits) { - write_index(idx, bits, START); - SIMDJSON_IF_CONSTEXPR(N > 1) { - write_indexes<(N - 1 > 0 ? START + 1 : START), (N - 1 >= 0 ? N - 1 : 1)>(idx, bits); - } - return START + N; - } + template + simdjson_inline int write_indexes(uint32_t idx, uint64_t& bits) { + write_index(idx, bits, START); + SIMDJSON_IF_CONSTEXPR (N > 1) { + write_indexes<(N-1>0?START+1:START), (N-1>=0?N-1:1)>(idx, bits); + } + return START+N; + } - template - simdjson_inline int write_indexes_stepped(uint32_t idx, uint64_t& bits, int cnt) { - write_indexes(idx, bits); - SIMDJSON_IF_CONSTEXPR((START + STEP) < END) { - if (simdjson_unlikely((START + STEP) < cnt)) { - write_indexes_stepped<(START + STEP < END ? START + STEP : END), END, STEP>(idx, bits, cnt); - } - } - return ((END - START) % STEP) == 0 ? END : (END - START) - ((END - START) % STEP) + STEP; - } + template + simdjson_inline int write_indexes_stepped(uint32_t idx, uint64_t& bits, int cnt) { + write_indexes(idx, bits); + SIMDJSON_IF_CONSTEXPR ((START+STEP) < END) { + if (simdjson_unlikely((START+STEP) < cnt)) { + write_indexes_stepped<(START+STEP(idx, bits, cnt); + } + } + return ((END-START) % STEP) == 0 ? END : (END-START) - ((END-START) % STEP) + STEP; + } - // flatten out values in 'bits' assuming that they are are to have values of idx - // plus their position in the bitvector, and store these indexes at - // base_ptr[base] incrementing base as we go - // will potentially store extra values beyond end of valid bits, so base_ptr - // needs to be large enough to handle this - // - // If the kernel sets SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER, then it - // will provide its own version of the code. + // flatten out values in 'bits' assuming that they are are to have values of idx + // plus their position in the bitvector, and store these indexes at + // base_ptr[base] incrementing base as we go + // will potentially store extra values beyond end of valid bits, so base_ptr + // needs to be large enough to handle this + // + // If the kernel sets SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER, then it + // will provide its own version of the code. #ifdef SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER - simdjson_inline void write(uint32_t idx, uint64_t bits); + simdjson_inline void write(uint32_t idx, uint64_t bits); #else - simdjson_inline void write(uint32_t idx, uint64_t bits) { - // In some instances, the next branch is expensive because it is mispredicted. - // Unfortunately, in other cases, - // it helps tremendously. - if (bits == 0) - return; + simdjson_inline void write(uint32_t idx, uint64_t bits) { + // In some instances, the next branch is expensive because it is mispredicted. + // Unfortunately, in other cases, + // it helps tremendously. + if (bits == 0) + return; - int cnt = static_cast(count_ones(bits)); + int cnt = static_cast(count_ones(bits)); #if SIMDJSON_PREFER_REVERSE_BITS - bits = reverse_bits(bits); + bits = reverse_bits(bits); #endif #ifdef SIMDJSON_STRUCTURAL_INDEXER_STEP - static constexpr const int STEP = SIMDJSON_STRUCTURAL_INDEXER_STEP; + static constexpr const int STEP = SIMDJSON_STRUCTURAL_INDEXER_STEP; #else - static constexpr const int STEP = 4; + static constexpr const int STEP = 4; #endif - static constexpr const int STEP_UNTIL = 24; + static constexpr const int STEP_UNTIL = 24; - write_indexes_stepped<0, STEP_UNTIL, STEP>(idx, bits, cnt); - SIMDJSON_IF_CONSTEXPR(STEP_UNTIL < 64) { - if (simdjson_unlikely(STEP_UNTIL < cnt)) { - for (int i = STEP_UNTIL; i < cnt; i++) { - write_index(idx, bits, i); - } - } - } + write_indexes_stepped<0, STEP_UNTIL, STEP>(idx, bits, cnt); + SIMDJSON_IF_CONSTEXPR (STEP_UNTIL < 64) { + if (simdjson_unlikely(STEP_UNTIL < cnt)) { + for (int i=STEP_UNTIL; itail += cnt; - } + this->tail += cnt; + } #endif // SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER - }; +}; - class json_structural_indexer { - public: - /** - * Find the important bits of JSON in a 128-byte chunk, and add them to structural_indexes. - * - * @param partial Setting the partial parameter to true allows the find_structural_bits to - * tolerate unclosed strings. The caller should still ensure that the input is valid UTF-8. If - * you are processing substrings, you may want to call on a function like trimmed_length_safe_utf8. - */ - template - static error_code index(const uint8_t* buf, size_t len, dom_parser_implementation& parser, stage1_mode partial) noexcept; +class json_structural_indexer { +public: + /** + * Find the important bits of JSON in a 128-byte chunk, and add them to structural_indexes. + * + * @param partial Setting the partial parameter to true allows the find_structural_bits to + * tolerate unclosed strings. The caller should still ensure that the input is valid UTF-8. If + * you are processing substrings, you may want to call on a function like trimmed_length_safe_utf8. + */ + template + static error_code index(const uint8_t *buf, size_t len, dom_parser_implementation &parser, stage1_mode partial) noexcept; - private: - simdjson_inline json_structural_indexer(uint32_t* structural_indexes); - template - simdjson_inline void step(const uint8_t* block, buf_block_reader& reader) noexcept; - simdjson_inline void next(const simd::simd8x64& in, const json_block& block, size_t idx); - simdjson_inline error_code finish(dom_parser_implementation& parser, size_t idx, size_t len, stage1_mode partial); +private: + simdjson_inline json_structural_indexer(uint32_t *structural_indexes); + template + simdjson_inline void step(const uint8_t *block, buf_block_reader &reader) noexcept; + simdjson_inline void next(const simd::simd8x64& in, const json_block& block, size_t idx); + simdjson_warn_unused simdjson_inline error_code finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial); - json_scanner scanner{}; - utf8_checker checker{}; - bit_indexer indexer; - uint64_t prev_structurals = 0; - uint64_t unescaped_chars_error = 0; - }; + json_scanner scanner{}; + utf8_checker checker{}; + bit_indexer indexer; + uint64_t prev_structurals = 0; + uint64_t unescaped_chars_error = 0; +}; - simdjson_inline json_structural_indexer::json_structural_indexer(uint32_t* structural_indexes) : indexer{ structural_indexes } {} +simdjson_inline json_structural_indexer::json_structural_indexer(uint32_t *structural_indexes) : indexer{structural_indexes} {} - // Skip the last character if it is partial - simdjson_inline size_t trim_partial_utf8(const uint8_t* buf, size_t len) { - if (simdjson_unlikely(len < 3)) { - switch (len) { - case 2: - if (buf[len - 1] >= 0xc0) { return len - 1; } // 2-, 3- and 4-byte characters with only 1 byte left - if (buf[len - 2] >= 0xe0) { return len - 2; } // 3- and 4-byte characters with only 2 bytes left - return len; - case 1: - if (buf[len - 1] >= 0xc0) { return len - 1; } // 2-, 3- and 4-byte characters with only 1 byte left - return len; - case 0: - return len; - } - } - if (buf[len - 1] >= 0xc0) { return len - 1; } // 2-, 3- and 4-byte characters with only 1 byte left - if (buf[len - 2] >= 0xe0) { return len - 2; } // 3- and 4-byte characters with only 1 byte left - if (buf[len - 3] >= 0xf0) { return len - 3; } // 4-byte characters with only 3 bytes left - return len; - } +// Skip the last character if it is partial +simdjson_inline size_t trim_partial_utf8(const uint8_t *buf, size_t len) { + if (simdjson_unlikely(len < 3)) { + switch (len) { + case 2: + if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left + if (buf[len-2] >= 0xe0) { return len-2; } // 3- and 4-byte characters with only 2 bytes left + return len; + case 1: + if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left + return len; + case 0: + return len; + } + } + if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left + if (buf[len-2] >= 0xe0) { return len-2; } // 3- and 4-byte characters with only 1 byte left + if (buf[len-3] >= 0xf0) { return len-3; } // 4-byte characters with only 3 bytes left + return len; +} - // - // PERF NOTES: - // We pipe 2 inputs through these stages: - // 1. Load JSON into registers. This takes a long time and is highly parallelizable, so we load - // 2 inputs' worth at once so that by the time step 2 is looking for them input, it's available. - // 2. Scan the JSON for critical data: strings, scalars and operators. This is the critical path. - // The output of step 1 depends entirely on this information. These functions don't quite use - // up enough CPU: the second half of the functions is highly serial, only using 1 execution core - // at a time. The second input's scans has some dependency on the first ones finishing it, but - // they can make a lot of progress before they need that information. - // 3. Step 1 does not use enough capacity, so we run some extra stuff while we're waiting for that - // to finish: utf-8 checks and generating the output from the last iteration. - // - // The reason we run 2 inputs at a time, is steps 2 and 3 are *still* not enough to soak up all - // available capacity with just one input. Running 2 at a time seems to give the CPU a good enough - // workout. - // - template - error_code json_structural_indexer::index(const uint8_t* buf, size_t len, dom_parser_implementation& parser, stage1_mode partial) noexcept { - if (simdjson_unlikely(len > parser.capacity())) { return CAPACITY; } - // We guard the rest of the code so that we can assume that len > 0 throughout. - if (len == 0) { return EMPTY; } - if (is_streaming(partial)) { - len = trim_partial_utf8(buf, len); - // If you end up with an empty window after trimming - // the partial UTF-8 bytes, then chances are good that you - // have an UTF-8 formatting error. - if (len == 0) { return UTF8_ERROR; } - } - buf_block_reader reader(buf, len); - json_structural_indexer indexer(parser.structural_indexes.get()); +// +// PERF NOTES: +// We pipe 2 inputs through these stages: +// 1. Load JSON into registers. This takes a long time and is highly parallelizable, so we load +// 2 inputs' worth at once so that by the time step 2 is looking for them input, it's available. +// 2. Scan the JSON for critical data: strings, scalars and operators. This is the critical path. +// The output of step 1 depends entirely on this information. These functions don't quite use +// up enough CPU: the second half of the functions is highly serial, only using 1 execution core +// at a time. The second input's scans has some dependency on the first ones finishing it, but +// they can make a lot of progress before they need that information. +// 3. Step 1 does not use enough capacity, so we run some extra stuff while we're waiting for that +// to finish: utf-8 checks and generating the output from the last iteration. +// +// The reason we run 2 inputs at a time, is steps 2 and 3 are *still* not enough to soak up all +// available capacity with just one input. Running 2 at a time seems to give the CPU a good enough +// workout. +// +template +error_code json_structural_indexer::index(const uint8_t *buf, size_t len, dom_parser_implementation &parser, stage1_mode partial) noexcept { + if (simdjson_unlikely(len > parser.capacity())) { return CAPACITY; } + // We guard the rest of the code so that we can assume that len > 0 throughout. + if (len == 0) { return EMPTY; } + if (is_streaming(partial)) { + len = trim_partial_utf8(buf, len); + // If you end up with an empty window after trimming + // the partial UTF-8 bytes, then chances are good that you + // have an UTF-8 formatting error. + if(len == 0) { return UTF8_ERROR; } + } + buf_block_reader reader(buf, len); + json_structural_indexer indexer(parser.structural_indexes.get()); - // Read all but the last block - while (reader.has_full_block()) { - indexer.step(reader.full_block(), reader); - } - // Take care of the last block (will always be there unless file is empty which is - // not supposed to happen.) - uint8_t block[STEP_SIZE]; - if (simdjson_unlikely(reader.get_remainder(block) == 0)) { return UNEXPECTED_ERROR; } - indexer.step(block, reader); - return indexer.finish(parser, reader.block_index(), len, partial); - } + // Read all but the last block + while (reader.has_full_block()) { + indexer.step(reader.full_block(), reader); + } + // Take care of the last block (will always be there unless file is empty which is + // not supposed to happen.) + uint8_t block[STEP_SIZE]; + if (simdjson_unlikely(reader.get_remainder(block) == 0)) { return UNEXPECTED_ERROR; } + indexer.step(block, reader); + return indexer.finish(parser, reader.block_index(), len, partial); +} - template<> - simdjson_inline void json_structural_indexer::step<128>(const uint8_t* block, buf_block_reader<128>& reader) noexcept { - simd::simd8x64 in_1(block); - simd::simd8x64 in_2(block + 64); - json_block block_1 = scanner.next(in_1); - json_block block_2 = scanner.next(in_2); - this->next(in_1, block_1, reader.block_index()); - this->next(in_2, block_2, reader.block_index() + 64); - reader.advance(); - } +template<> +simdjson_inline void json_structural_indexer::step<128>(const uint8_t *block, buf_block_reader<128> &reader) noexcept { + simd::simd8x64 in_1(block); + simd::simd8x64 in_2(block+64); + json_block block_1 = scanner.next(in_1); + json_block block_2 = scanner.next(in_2); + this->next(in_1, block_1, reader.block_index()); + this->next(in_2, block_2, reader.block_index()+64); + reader.advance(); +} - template<> - simdjson_inline void json_structural_indexer::step<64>(const uint8_t* block, buf_block_reader<64>& reader) noexcept { - simd::simd8x64 in_1(block); - json_block block_1 = scanner.next(in_1); - this->next(in_1, block_1, reader.block_index()); - reader.advance(); - } +template<> +simdjson_inline void json_structural_indexer::step<64>(const uint8_t *block, buf_block_reader<64> &reader) noexcept { + simd::simd8x64 in_1(block); + json_block block_1 = scanner.next(in_1); + this->next(in_1, block_1, reader.block_index()); + reader.advance(); +} - simdjson_inline void json_structural_indexer::next(const simd::simd8x64& in, const json_block& block, size_t idx) { - uint64_t unescaped = in.lteq(0x1F); +simdjson_inline void json_structural_indexer::next(const simd::simd8x64& in, const json_block& block, size_t idx) { + uint64_t unescaped = in.lteq(0x1F); #if SIMDJSON_UTF8VALIDATION - checker.check_next_input(in); + checker.check_next_input(in); #endif - indexer.write(uint32_t(idx - 64), prev_structurals); // Output *last* iteration's structurals to the parser - prev_structurals = block.structural_start(); - unescaped_chars_error |= block.non_quote_inside_string(unescaped); - } + indexer.write(uint32_t(idx-64), prev_structurals); // Output *last* iteration's structurals to the parser + prev_structurals = block.structural_start(); + unescaped_chars_error |= block.non_quote_inside_string(unescaped); +} - simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementation& parser, size_t idx, size_t len, stage1_mode partial) { - // Write out the final iteration's structurals - indexer.write(uint32_t(idx - 64), prev_structurals); - error_code error = scanner.finish(); - // We deliberately break down the next expression so that it is - // human readable. - const bool should_we_exit = is_streaming(partial) ? - ((error != SUCCESS) && (error != UNCLOSED_STRING)) // when partial we tolerate UNCLOSED_STRING - : (error != SUCCESS); // if partial is false, we must have SUCCESS - const bool have_unclosed_string = (error == UNCLOSED_STRING); - if (simdjson_unlikely(should_we_exit)) { return error; } +simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial) { + // Write out the final iteration's structurals + indexer.write(uint32_t(idx-64), prev_structurals); + error_code error = scanner.finish(); + // We deliberately break down the next expression so that it is + // human readable. + const bool should_we_exit = is_streaming(partial) ? + ((error != SUCCESS) && (error != UNCLOSED_STRING)) // when partial we tolerate UNCLOSED_STRING + : (error != SUCCESS); // if partial is false, we must have SUCCESS + const bool have_unclosed_string = (error == UNCLOSED_STRING); + if (simdjson_unlikely(should_we_exit)) { return error; } - if (unescaped_chars_error) { - return UNESCAPED_CHARS; - } - parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get()); - /*** - * The On-Demand API requires special padding. - * - * This is related to https://github.com/simdjson/simdjson/issues/906 - * Basically, we want to make sure that if the parsing continues beyond the last (valid) - * structural character, it quickly stops. - * Only three structural characters can be repeated without triggering an error in JSON: [,] and }. - * We repeat the padding character (at 'len'). We don't know what it is, but if the parsing - * continues, then it must be [,] or }. - * Suppose it is ] or }. We backtrack to the first character, what could it be that would - * not trigger an error? It could be ] or } but no, because you can't start a document that way. - * It can't be a comma, a colon or any simple value. So the only way we could continue is - * if the repeated character is [. But if so, the document must start with [. But if the document - * starts with [, it should end with ]. If we enforce that rule, then we would get - * ][[ which is invalid. - * - * This is illustrated with the test array_iterate_unclosed_error() on the following input: - * R"({ "a": [,,)" - **/ - parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); // used later in partial == stage1_mode::streaming_final - parser.structural_indexes[parser.n_structural_indexes + 1] = uint32_t(len); - parser.structural_indexes[parser.n_structural_indexes + 2] = 0; - parser.next_structural_index = 0; - // a valid JSON file cannot have zero structural indexes - we should have found something - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { - return EMPTY; - } - if (simdjson_unlikely(parser.structural_indexes[parser.n_structural_indexes - 1] > len)) { - return UNEXPECTED_ERROR; - } - if (partial == stage1_mode::streaming_partial) { - // If we have an unclosed string, then the last structural - // will be the quote and we want to make sure to omit it. - if (have_unclosed_string) { - parser.n_structural_indexes--; - // a valid JSON file cannot have zero structural indexes - we should have found something - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; } - } - // We truncate the input to the end of the last complete document (or zero). - auto new_structural_indexes = find_next_document_index(parser); - if (new_structural_indexes == 0 && parser.n_structural_indexes > 0) { - if (parser.structural_indexes[0] == 0) { - // If the buffer is partial and we started at index 0 but the document is - // incomplete, it's too big to parse. - return CAPACITY; - } - else { - // It is possible that the document could be parsed, we just had a lot - // of white space. - parser.n_structural_indexes = 0; - return EMPTY; - } - } + if (unescaped_chars_error) { + return UNESCAPED_CHARS; + } + parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get()); + /*** + * The On-Demand API requires special padding. + * + * This is related to https://github.com/simdjson/simdjson/issues/906 + * Basically, we want to make sure that if the parsing continues beyond the last (valid) + * structural character, it quickly stops. + * Only three structural characters can be repeated without triggering an error in JSON: [,] and }. + * We repeat the padding character (at 'len'). We don't know what it is, but if the parsing + * continues, then it must be [,] or }. + * Suppose it is ] or }. We backtrack to the first character, what could it be that would + * not trigger an error? It could be ] or } but no, because you can't start a document that way. + * It can't be a comma, a colon or any simple value. So the only way we could continue is + * if the repeated character is [. But if so, the document must start with [. But if the document + * starts with [, it should end with ]. If we enforce that rule, then we would get + * ][[ which is invalid. + * + * This is illustrated with the test array_iterate_unclosed_error() on the following input: + * R"({ "a": [,,)" + **/ + parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); // used later in partial == stage1_mode::streaming_final + parser.structural_indexes[parser.n_structural_indexes + 1] = uint32_t(len); + parser.structural_indexes[parser.n_structural_indexes + 2] = 0; + parser.next_structural_index = 0; + // a valid JSON file cannot have zero structural indexes - we should have found something + if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { + return EMPTY; + } + if (simdjson_unlikely(parser.structural_indexes[parser.n_structural_indexes - 1] > len)) { + return UNEXPECTED_ERROR; + } + if (partial == stage1_mode::streaming_partial) { + // If we have an unclosed string, then the last structural + // will be the quote and we want to make sure to omit it. + if(have_unclosed_string) { + parser.n_structural_indexes--; + // a valid JSON file cannot have zero structural indexes - we should have found something + if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; } + } + // We truncate the input to the end of the last complete document (or zero). + auto new_structural_indexes = find_next_document_index(parser); + if (new_structural_indexes == 0 && parser.n_structural_indexes > 0) { + if(parser.structural_indexes[0] == 0) { + // If the buffer is partial and we started at index 0 but the document is + // incomplete, it's too big to parse. + return CAPACITY; + } else { + // It is possible that the document could be parsed, we just had a lot + // of white space. + parser.n_structural_indexes = 0; + return EMPTY; + } + } - parser.n_structural_indexes = new_structural_indexes; - } - else if (partial == stage1_mode::streaming_final) { - if (have_unclosed_string) { parser.n_structural_indexes--; } - // We truncate the input to the end of the last complete document (or zero). - // Because partial == stage1_mode::streaming_final, it means that we may - // silently ignore trailing garbage. Though it sounds bad, we do it - // deliberately because many people who have streams of JSON documents - // will truncate them for processing. E.g., imagine that you are uncompressing - // the data from a size file or receiving it in chunks from the network. You - // may not know where exactly the last document will be. Meanwhile the - // document_stream instances allow people to know the JSON documents they are - // parsing (see the iterator.source() method). - parser.n_structural_indexes = find_next_document_index(parser); - // We store the initial n_structural_indexes so that the client can see - // whether we used truncation. If initial_n_structural_indexes == parser.n_structural_indexes, - // then this will query parser.structural_indexes[parser.n_structural_indexes] which is len, - // otherwise, it will copy some prior index. - parser.structural_indexes[parser.n_structural_indexes + 1] = parser.structural_indexes[parser.n_structural_indexes]; - // This next line is critical, do not change it unless you understand what you are - // doing. - parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { - // We tolerate an unclosed string at the very end of the stream. Indeed, users - // often load their data in bulk without being careful and they want us to ignore - // the trailing garbage. - return EMPTY; - } - } - checker.check_eof(); - return checker.errors(); - } + parser.n_structural_indexes = new_structural_indexes; + } else if (partial == stage1_mode::streaming_final) { + if(have_unclosed_string) { parser.n_structural_indexes--; } + // We truncate the input to the end of the last complete document (or zero). + // Because partial == stage1_mode::streaming_final, it means that we may + // silently ignore trailing garbage. Though it sounds bad, we do it + // deliberately because many people who have streams of JSON documents + // will truncate them for processing. E.g., imagine that you are uncompressing + // the data from a size file or receiving it in chunks from the network. You + // may not know where exactly the last document will be. Meanwhile the + // document_stream instances allow people to know the JSON documents they are + // parsing (see the iterator.source() method). + parser.n_structural_indexes = find_next_document_index(parser); + // We store the initial n_structural_indexes so that the client can see + // whether we used truncation. If initial_n_structural_indexes == parser.n_structural_indexes, + // then this will query parser.structural_indexes[parser.n_structural_indexes] which is len, + // otherwise, it will copy some prior index. + parser.structural_indexes[parser.n_structural_indexes + 1] = parser.structural_indexes[parser.n_structural_indexes]; + // This next line is critical, do not change it unless you understand what you are + // doing. + parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); + if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { + // We tolerate an unclosed string at the very end of the stream. Indeed, users + // often load their data in bulk without being careful and they want us to ignore + // the trailing garbage. + return EMPTY; + } + } + checker.check_eof(); + return checker.errors(); +} - } // namespace stage1 - } // unnamed namespace - } // namespace ppc64 +} // namespace stage1 +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson // Clear CUSTOM_BIT_INDEXER so other implementations can set it if they need to. @@ -31737,38 +33408,38 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { - namespace { - namespace stage1 { +namespace ppc64 { +namespace { +namespace stage1 { - /** - * Validates that the string is actual UTF-8. - */ - template - bool generic_validate_utf8(const uint8_t* input, size_t length) { - checker c{}; - buf_block_reader<64> reader(input, length); - while (reader.has_full_block()) { - simd::simd8x64 in(reader.full_block()); - c.check_next_input(in); - reader.advance(); - } - uint8_t block[64]{}; - reader.get_remainder(block); - simd::simd8x64 in(block); - c.check_next_input(in); - reader.advance(); - c.check_eof(); - return c.errors() == error_code::SUCCESS; - } +/** + * Validates that the string is actual UTF-8. + */ +template +bool generic_validate_utf8(const uint8_t * input, size_t length) { + checker c{}; + buf_block_reader<64> reader(input, length); + while (reader.has_full_block()) { + simd::simd8x64 in(reader.full_block()); + c.check_next_input(in); + reader.advance(); + } + uint8_t block[64]{}; + reader.get_remainder(block); + simd::simd8x64 in(block); + c.check_next_input(in); + reader.advance(); + c.check_eof(); + return c.errors() == error_code::SUCCESS; +} - bool generic_validate_utf8(const char* input, size_t length) { - return generic_validate_utf8(reinterpret_cast(input), length); - } +bool generic_validate_utf8(const char * input, size_t length) { + return generic_validate_utf8(reinterpret_cast(input),length); +} - } // namespace stage1 - } // unnamed namespace - } // namespace ppc64 +} // namespace stage1 +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_UTF8_VALIDATOR_H @@ -31787,18 +33458,18 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { - namespace { - namespace stage2 { +namespace ppc64 { +namespace { +namespace stage2 { - class json_iterator; - class structural_iterator; - struct tape_builder; - struct tape_writer; +class json_iterator; +class structural_iterator; +struct tape_builder; +struct tape_writer; - } // namespace stage2 - } // unnamed namespace - } // namespace ppc64 +} // namespace stage2 +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_BASE_H @@ -31816,109 +33487,109 @@ namespace simdjson { #include namespace simdjson { - namespace ppc64 { - namespace { - namespace stage2 { +namespace ppc64 { +namespace { +namespace stage2 { - struct tape_writer { - /** The next place to write to tape */ - uint64_t* next_tape_loc; +struct tape_writer { + /** The next place to write to tape */ + uint64_t *next_tape_loc; - /** Write a signed 64-bit value to tape. */ - simdjson_inline void append_s64(int64_t value) noexcept; + /** Write a signed 64-bit value to tape. */ + simdjson_inline void append_s64(int64_t value) noexcept; - /** Write an unsigned 64-bit value to tape. */ - simdjson_inline void append_u64(uint64_t value) noexcept; + /** Write an unsigned 64-bit value to tape. */ + simdjson_inline void append_u64(uint64_t value) noexcept; - /** Write a double value to tape. */ - simdjson_inline void append_double(double value) noexcept; + /** Write a double value to tape. */ + simdjson_inline void append_double(double value) noexcept; - /** - * Append a tape entry (an 8-bit type,and 56 bits worth of value). - */ - simdjson_inline void append(uint64_t val, internal::tape_type t) noexcept; + /** + * Append a tape entry (an 8-bit type,and 56 bits worth of value). + */ + simdjson_inline void append(uint64_t val, internal::tape_type t) noexcept; - /** - * Skip the current tape entry without writing. - * - * Used to skip the start of the container, since we'll come back later to fill it in when the - * container ends. - */ - simdjson_inline void skip() noexcept; + /** + * Skip the current tape entry without writing. + * + * Used to skip the start of the container, since we'll come back later to fill it in when the + * container ends. + */ + simdjson_inline void skip() noexcept; - /** - * Skip the number of tape entries necessary to write a large u64 or i64. - */ - simdjson_inline void skip_large_integer() noexcept; + /** + * Skip the number of tape entries necessary to write a large u64 or i64. + */ + simdjson_inline void skip_large_integer() noexcept; - /** - * Skip the number of tape entries necessary to write a double. - */ - simdjson_inline void skip_double() noexcept; + /** + * Skip the number of tape entries necessary to write a double. + */ + simdjson_inline void skip_double() noexcept; - /** - * Write a value to a known location on tape. - * - * Used to go back and write out the start of a container after the container ends. - */ - simdjson_inline static void write(uint64_t& tape_loc, uint64_t val, internal::tape_type t) noexcept; + /** + * Write a value to a known location on tape. + * + * Used to go back and write out the start of a container after the container ends. + */ + simdjson_inline static void write(uint64_t &tape_loc, uint64_t val, internal::tape_type t) noexcept; - private: - /** - * Append both the tape entry, and a supplementary value following it. Used for types that need - * all 64 bits, such as double and uint64_t. - */ - template - simdjson_inline void append2(uint64_t val, T val2, internal::tape_type t) noexcept; - }; // struct tape_writer +private: + /** + * Append both the tape entry, and a supplementary value following it. Used for types that need + * all 64 bits, such as double and uint64_t. + */ + template + simdjson_inline void append2(uint64_t val, T val2, internal::tape_type t) noexcept; +}; // struct tape_writer - simdjson_inline void tape_writer::append_s64(int64_t value) noexcept { - append2(0, value, internal::tape_type::INT64); - } +simdjson_inline void tape_writer::append_s64(int64_t value) noexcept { + append2(0, value, internal::tape_type::INT64); +} - simdjson_inline void tape_writer::append_u64(uint64_t value) noexcept { - append(0, internal::tape_type::UINT64); - *next_tape_loc = value; - next_tape_loc++; - } +simdjson_inline void tape_writer::append_u64(uint64_t value) noexcept { + append(0, internal::tape_type::UINT64); + *next_tape_loc = value; + next_tape_loc++; +} - /** Write a double value to tape. */ - simdjson_inline void tape_writer::append_double(double value) noexcept { - append2(0, value, internal::tape_type::DOUBLE); - } +/** Write a double value to tape. */ +simdjson_inline void tape_writer::append_double(double value) noexcept { + append2(0, value, internal::tape_type::DOUBLE); +} - simdjson_inline void tape_writer::skip() noexcept { - next_tape_loc++; - } +simdjson_inline void tape_writer::skip() noexcept { + next_tape_loc++; +} - simdjson_inline void tape_writer::skip_large_integer() noexcept { - next_tape_loc += 2; - } +simdjson_inline void tape_writer::skip_large_integer() noexcept { + next_tape_loc += 2; +} - simdjson_inline void tape_writer::skip_double() noexcept { - next_tape_loc += 2; - } +simdjson_inline void tape_writer::skip_double() noexcept { + next_tape_loc += 2; +} - simdjson_inline void tape_writer::append(uint64_t val, internal::tape_type t) noexcept { - *next_tape_loc = val | ((uint64_t(char(t))) << 56); - next_tape_loc++; - } +simdjson_inline void tape_writer::append(uint64_t val, internal::tape_type t) noexcept { + *next_tape_loc = val | ((uint64_t(char(t))) << 56); + next_tape_loc++; +} - template - simdjson_inline void tape_writer::append2(uint64_t val, T val2, internal::tape_type t) noexcept { - append(val, t); - static_assert(sizeof(val2) == sizeof(*next_tape_loc), "Type is not 64 bits!"); - memcpy(next_tape_loc, &val2, sizeof(val2)); - next_tape_loc++; - } +template +simdjson_inline void tape_writer::append2(uint64_t val, T val2, internal::tape_type t) noexcept { + append(val, t); + static_assert(sizeof(val2) == sizeof(*next_tape_loc), "Type is not 64 bits!"); + memcpy(next_tape_loc, &val2, sizeof(val2)); + next_tape_loc++; +} - simdjson_inline void tape_writer::write(uint64_t& tape_loc, uint64_t val, internal::tape_type t) noexcept { - tape_loc = val | ((uint64_t(char(t))) << 56); - } +simdjson_inline void tape_writer::write(uint64_t &tape_loc, uint64_t val, internal::tape_type t) noexcept { + tape_loc = val | ((uint64_t(char(t))) << 56); +} - } // namespace stage2 - } // unnamed namespace - } // namespace ppc64 +} // namespace stage2 +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_WRITER_H @@ -31938,92 +33609,90 @@ namespace simdjson { // This is for an internal-only stage 2 specific logger. // Set LOG_ENABLED = true to log what stage 2 is doing! namespace simdjson { - namespace ppc64 { - namespace { - namespace logger { +namespace ppc64 { +namespace { +namespace logger { - static constexpr const char* DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"; + static constexpr const char * DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"; #if SIMDJSON_VERBOSE_LOGGING - static constexpr const bool LOG_ENABLED = true; + static constexpr const bool LOG_ENABLED = true; #else - static constexpr const bool LOG_ENABLED = false; + static constexpr const bool LOG_ENABLED = false; #endif - 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 constexpr const int LOG_INDEX_LEN = 5; + 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 constexpr const int LOG_INDEX_LEN = 5; - static int log_depth; // Not threadsafe. Log only. + static int log_depth; // Not threadsafe. Log only. - // Helper to turn unprintable or newline characters into spaces - static simdjson_inline char printable_char(char c) { - if (c >= 0x20) { - return c; - } - else { - return ' '; - } - } + // Helper to turn unprintable or newline characters into spaces + static simdjson_inline char printable_char(char c) { + if (c >= 0x20) { + return c; + } else { + return ' '; + } + } - // Print the header and set up log_start - static simdjson_inline void log_start() { - if (LOG_ENABLED) { - log_depth = 0; - printf("\n"); - printf("| %-*s | %-*s | %-*s | %-*s | Detail |\n", LOG_EVENT_LEN, "Event", LOG_BUFFER_LEN, "Buffer", LOG_SMALL_BUFFER_LEN, "Next", 5, "Next#"); - printf("|%.*s|%.*s|%.*s|%.*s|--------|\n", LOG_EVENT_LEN + 2, DASHES, LOG_BUFFER_LEN + 2, DASHES, LOG_SMALL_BUFFER_LEN + 2, DASHES, 5 + 2, DASHES); - } - } + // Print the header and set up log_start + static simdjson_inline void log_start() { + if (LOG_ENABLED) { + log_depth = 0; + printf("\n"); + printf("| %-*s | %-*s | %-*s | %-*s | Detail |\n", LOG_EVENT_LEN, "Event", LOG_BUFFER_LEN, "Buffer", LOG_SMALL_BUFFER_LEN, "Next", 5, "Next#"); + printf("|%.*s|%.*s|%.*s|%.*s|--------|\n", LOG_EVENT_LEN+2, DASHES, LOG_BUFFER_LEN+2, DASHES, LOG_SMALL_BUFFER_LEN+2, DASHES, 5+2, DASHES); + } + } - simdjson_unused static simdjson_inline void log_string(const char* message) { - if (LOG_ENABLED) { - printf("%s\n", message); - } - } + simdjson_unused static simdjson_inline void log_string(const char *message) { + if (LOG_ENABLED) { + printf("%s\n", message); + } + } - // Logs a single line from the stage 2 DOM parser - template - static simdjson_inline void log_line(S& structurals, const char* title_prefix, const char* title, const char* detail) { - if (LOG_ENABLED) { - printf("| %*s%s%-*s ", log_depth * 2, "", title_prefix, LOG_EVENT_LEN - log_depth * 2 - int(strlen(title_prefix)), title); - auto current_index = structurals.at_beginning() ? nullptr : structurals.next_structural - 1; - auto next_index = structurals.next_structural; - auto current = current_index ? &structurals.buf[*current_index] : reinterpret_cast(" "); - auto next = &structurals.buf[*next_index]; - { - // Print the next N characters in the buffer. - printf("| "); - // Otherwise, print the characters starting from the buffer position. - // Print spaces for unprintable or newline characters. - for (int i = 0; i < LOG_BUFFER_LEN; i++) { - printf("%c", printable_char(current[i])); - } - printf(" "); - // Print the next N characters in the buffer. - printf("| "); - // Otherwise, print the characters starting from the buffer position. - // Print spaces for unprintable or newline characters. - for (int i = 0; i < LOG_SMALL_BUFFER_LEN; i++) { - printf("%c", printable_char(next[i])); - } - printf(" "); - } - if (current_index) { - printf("| %*u ", LOG_INDEX_LEN, *current_index); - } - else { - printf("| %-*s ", LOG_INDEX_LEN, ""); - } - // printf("| %*u ", LOG_INDEX_LEN, structurals.next_tape_index()); - printf("| %-s ", detail); - printf("|\n"); - } - } + // Logs a single line from the stage 2 DOM parser + template + static simdjson_inline void log_line(S &structurals, const char *title_prefix, const char *title, const char *detail) { + if (LOG_ENABLED) { + printf("| %*s%s%-*s ", log_depth*2, "", title_prefix, LOG_EVENT_LEN - log_depth*2 - int(strlen(title_prefix)), title); + auto current_index = structurals.at_beginning() ? nullptr : structurals.next_structural-1; + auto next_index = structurals.next_structural; + auto current = current_index ? &structurals.buf[*current_index] : reinterpret_cast(" "); + auto next = &structurals.buf[*next_index]; + { + // Print the next N characters in the buffer. + printf("| "); + // Otherwise, print the characters starting from the buffer position. + // Print spaces for unprintable or newline characters. + for (int i=0;i - simdjson_warn_unused simdjson_inline error_code walk_document(V& visitor) noexcept; + /** + * Walk the JSON document. + * + * The visitor receives callbacks when values are encountered. All callbacks pass the iterator as + * the first parameter; some callbacks have other parameters as well: + * + * - visit_document_start() - at the beginning. + * - visit_document_end() - at the end (if things were successful). + * + * - visit_array_start() - at the start `[` of a non-empty array. + * - visit_array_end() - at the end `]` of a non-empty array. + * - visit_empty_array() - when an empty array is encountered. + * + * - visit_object_end() - at the start `]` of a non-empty object. + * - visit_object_start() - at the end `]` of a non-empty object. + * - visit_empty_object() - when an empty object is encountered. + * - visit_key(const uint8_t *key) - when a key in an object field is encountered. key is + * guaranteed to point at the first quote of the string (`"key"`). + * - visit_primitive(const uint8_t *value) - when a value is a string, number, boolean or null. + * - visit_root_primitive(iter, uint8_t *value) - when the top-level value is a string, number, boolean or null. + * + * - increment_count(iter) - each time a value is found in an array or object. + */ + template + simdjson_warn_unused simdjson_inline error_code walk_document(V &visitor) noexcept; - /** - * Create an iterator capable of walking a JSON document. - * - * The document must have already passed through stage 1. - */ - simdjson_inline json_iterator(dom_parser_implementation& _dom_parser, size_t start_structural_index); + /** + * Create an iterator capable of walking a JSON document. + * + * The document must have already passed through stage 1. + */ + simdjson_inline json_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index); - /** - * Look at the next token. - * - * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). - * - * They may include invalid JSON as well (such as `1.2.3` or `ture`). - */ - simdjson_inline const uint8_t* peek() const noexcept; - /** - * Advance to the next token. - * - * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). - * - * They may include invalid JSON as well (such as `1.2.3` or `ture`). - */ - simdjson_inline const uint8_t* advance() noexcept; - /** - * Get the remaining length of the document, from the start of the current token. - */ - simdjson_inline size_t remaining_len() const noexcept; - /** - * Check if we are at the end of the document. - * - * If this is true, there are no more tokens. - */ - simdjson_inline bool at_eof() const noexcept; - /** - * Check if we are at the beginning of the document. - */ - simdjson_inline bool at_beginning() const noexcept; - simdjson_inline uint8_t last_structural() const noexcept; + /** + * Look at the next token. + * + * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). + * + * They may include invalid JSON as well (such as `1.2.3` or `ture`). + */ + simdjson_inline const uint8_t *peek() const noexcept; + /** + * Advance to the next token. + * + * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). + * + * They may include invalid JSON as well (such as `1.2.3` or `ture`). + */ + simdjson_inline const uint8_t *advance() noexcept; + /** + * Get the remaining length of the document, from the start of the current token. + */ + simdjson_inline size_t remaining_len() const noexcept; + /** + * Check if we are at the end of the document. + * + * If this is true, there are no more tokens. + */ + simdjson_inline bool at_eof() const noexcept; + /** + * Check if we are at the beginning of the document. + */ + simdjson_inline bool at_beginning() const noexcept; + simdjson_inline uint8_t last_structural() const noexcept; - /** - * Log that a value has been found. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_value(const char* type) const noexcept; - /** - * Log the start of a multipart value. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_start_value(const char* type) const noexcept; - /** - * Log the end of a multipart value. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_end_value(const char* type) const noexcept; - /** - * Log an error. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_error(const char* error) const noexcept; + /** + * Log that a value has been found. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_value(const char *type) const noexcept; + /** + * Log the start of a multipart value. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_start_value(const char *type) const noexcept; + /** + * Log the end of a multipart value. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_end_value(const char *type) const noexcept; + /** + * Log an error. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_error(const char *error) const noexcept; - template - simdjson_warn_unused simdjson_inline error_code visit_root_primitive(V& visitor, const uint8_t* value) noexcept; - template - simdjson_warn_unused simdjson_inline error_code visit_primitive(V& visitor, const uint8_t* value) noexcept; - }; + template + simdjson_warn_unused simdjson_inline error_code visit_root_primitive(V &visitor, const uint8_t *value) noexcept; + template + simdjson_warn_unused simdjson_inline error_code visit_primitive(V &visitor, const uint8_t *value) noexcept; +}; - template - simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V& visitor) noexcept { - logger::log_start(); +template +simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &visitor) noexcept { + logger::log_start(); - // - // Start the document - // - if (at_eof()) { return EMPTY; } - log_start_value("document"); - SIMDJSON_TRY(visitor.visit_document_start(*this)); + // + // Start the document + // + if (at_eof()) { return EMPTY; } + log_start_value("document"); + SIMDJSON_TRY( visitor.visit_document_start(*this) ); - // - // Read first value - // - { - auto value = advance(); + // + // Read first value + // + { + auto value = advance(); - // Make sure the outer object or array is closed before continuing; otherwise, there are ways we - // could get into memory corruption. See https://github.com/simdjson/simdjson/issues/906 - if (!STREAMING) { - switch (*value) { - case '{': if (last_structural() != '}') { log_value("starting brace unmatched"); return TAPE_ERROR; }; break; - case '[': if (last_structural() != ']') { log_value("starting bracket unmatched"); return TAPE_ERROR; }; break; - } - } + // Make sure the outer object or array is closed before continuing; otherwise, there are ways we + // could get into memory corruption. See https://github.com/simdjson/simdjson/issues/906 + if (!STREAMING) { + switch (*value) { + case '{': if (last_structural() != '}') { log_value("starting brace unmatched"); return TAPE_ERROR; }; break; + case '[': if (last_structural() != ']') { log_value("starting bracket unmatched"); return TAPE_ERROR; }; break; + } + } - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY(visitor.visit_empty_object(*this)); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY(visitor.visit_empty_array(*this)); break; } goto array_begin; - default: SIMDJSON_TRY(visitor.visit_root_primitive(*this, value)); break; - } - } - goto document_end; + switch (*value) { + case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; + case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; + default: SIMDJSON_TRY( visitor.visit_root_primitive(*this, value) ); break; + } + } + goto document_end; - // - // Object parser states - // - object_begin: - log_start_value("object"); - depth++; - if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } - dom_parser.is_array[depth] = false; - SIMDJSON_TRY(visitor.visit_object_start(*this)); +// +// Object parser states +// +object_begin: + log_start_value("object"); + depth++; + if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } + dom_parser.is_array[depth] = false; + SIMDJSON_TRY( visitor.visit_object_start(*this) ); - { - auto key = advance(); - if (*key != '"') { log_error("Object does not start with a key"); return TAPE_ERROR; } - SIMDJSON_TRY(visitor.increment_count(*this)); - SIMDJSON_TRY(visitor.visit_key(*this, key)); - } + { + auto key = advance(); + if (*key != '"') { log_error("Object does not start with a key"); return TAPE_ERROR; } + SIMDJSON_TRY( visitor.increment_count(*this) ); + SIMDJSON_TRY( visitor.visit_key(*this, key) ); + } - object_field: - if (simdjson_unlikely(*advance() != ':')) { log_error("Missing colon after key in object"); return TAPE_ERROR; } - { - auto value = advance(); - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY(visitor.visit_empty_object(*this)); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY(visitor.visit_empty_array(*this)); break; } goto array_begin; - default: SIMDJSON_TRY(visitor.visit_primitive(*this, value)); break; - } - } +object_field: + if (simdjson_unlikely( *advance() != ':' )) { log_error("Missing colon after key in object"); return TAPE_ERROR; } + { + auto value = advance(); + switch (*value) { + case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; + case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; + default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break; + } + } - object_continue: - switch (*advance()) { - case ',': - SIMDJSON_TRY(visitor.increment_count(*this)); - { - auto key = advance(); - if (simdjson_unlikely(*key != '"')) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; } - SIMDJSON_TRY(visitor.visit_key(*this, key)); - } - goto object_field; - case '}': log_end_value("object"); SIMDJSON_TRY(visitor.visit_object_end(*this)); goto scope_end; - default: log_error("No comma between object fields"); return TAPE_ERROR; - } +object_continue: + switch (*advance()) { + case ',': + SIMDJSON_TRY( visitor.increment_count(*this) ); + { + auto key = advance(); + if (simdjson_unlikely( *key != '"' )) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; } + SIMDJSON_TRY( visitor.visit_key(*this, key) ); + } + goto object_field; + case '}': log_end_value("object"); SIMDJSON_TRY( visitor.visit_object_end(*this) ); goto scope_end; + default: log_error("No comma between object fields"); return TAPE_ERROR; + } - scope_end: - depth--; - if (depth == 0) { goto document_end; } - if (dom_parser.is_array[depth]) { goto array_continue; } - goto object_continue; +scope_end: + depth--; + if (depth == 0) { goto document_end; } + if (dom_parser.is_array[depth]) { goto array_continue; } + goto object_continue; - // - // Array parser states - // - array_begin: - log_start_value("array"); - depth++; - if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } - dom_parser.is_array[depth] = true; - SIMDJSON_TRY(visitor.visit_array_start(*this)); - SIMDJSON_TRY(visitor.increment_count(*this)); +// +// Array parser states +// +array_begin: + log_start_value("array"); + depth++; + if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } + dom_parser.is_array[depth] = true; + SIMDJSON_TRY( visitor.visit_array_start(*this) ); + SIMDJSON_TRY( visitor.increment_count(*this) ); - array_value: - { - auto value = advance(); - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY(visitor.visit_empty_object(*this)); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY(visitor.visit_empty_array(*this)); break; } goto array_begin; - default: SIMDJSON_TRY(visitor.visit_primitive(*this, value)); break; - } - } +array_value: + { + auto value = advance(); + switch (*value) { + case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; + case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; + default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break; + } + } - array_continue: - switch (*advance()) { - case ',': SIMDJSON_TRY(visitor.increment_count(*this)); goto array_value; - case ']': log_end_value("array"); SIMDJSON_TRY(visitor.visit_array_end(*this)); goto scope_end; - default: log_error("Missing comma between array values"); return TAPE_ERROR; - } +array_continue: + switch (*advance()) { + case ',': SIMDJSON_TRY( visitor.increment_count(*this) ); goto array_value; + case ']': log_end_value("array"); SIMDJSON_TRY( visitor.visit_array_end(*this) ); goto scope_end; + default: log_error("Missing comma between array values"); return TAPE_ERROR; + } - document_end: - log_end_value("document"); - SIMDJSON_TRY(visitor.visit_document_end(*this)); +document_end: + log_end_value("document"); + SIMDJSON_TRY( visitor.visit_document_end(*this) ); - dom_parser.next_structural_index = uint32_t(next_structural - &dom_parser.structural_indexes[0]); + dom_parser.next_structural_index = uint32_t(next_structural - &dom_parser.structural_indexes[0]); - // If we didn't make it to the end, it's an error - if (!STREAMING && dom_parser.next_structural_index != dom_parser.n_structural_indexes) { - log_error("More than one JSON value at the root of the document, or extra characters at the end of the JSON!"); - return TAPE_ERROR; - } + // If we didn't make it to the end, it's an error + if ( !STREAMING && dom_parser.next_structural_index != dom_parser.n_structural_indexes ) { + log_error("More than one JSON value at the root of the document, or extra characters at the end of the JSON!"); + return TAPE_ERROR; + } - return SUCCESS; + return SUCCESS; - } // walk_document() +} // walk_document() - simdjson_inline json_iterator::json_iterator(dom_parser_implementation& _dom_parser, size_t start_structural_index) - : buf{ _dom_parser.buf }, - next_structural{ &_dom_parser.structural_indexes[start_structural_index] }, - dom_parser{ _dom_parser } { - } +simdjson_inline json_iterator::json_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index) + : buf{_dom_parser.buf}, + next_structural{&_dom_parser.structural_indexes[start_structural_index]}, + dom_parser{_dom_parser} { +} - simdjson_inline const uint8_t* json_iterator::peek() const noexcept { - return &buf[*(next_structural)]; - } - simdjson_inline const uint8_t* json_iterator::advance() noexcept { - return &buf[*(next_structural++)]; - } - simdjson_inline size_t json_iterator::remaining_len() const noexcept { - return dom_parser.len - *(next_structural - 1); - } +simdjson_inline const uint8_t *json_iterator::peek() const noexcept { + return &buf[*(next_structural)]; +} +simdjson_inline const uint8_t *json_iterator::advance() noexcept { + return &buf[*(next_structural++)]; +} +simdjson_inline size_t json_iterator::remaining_len() const noexcept { + return dom_parser.len - *(next_structural-1); +} - simdjson_inline bool json_iterator::at_eof() const noexcept { - return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; - } - simdjson_inline bool json_iterator::at_beginning() const noexcept { - return next_structural == dom_parser.structural_indexes.get(); - } - simdjson_inline uint8_t json_iterator::last_structural() const noexcept { - return buf[dom_parser.structural_indexes[dom_parser.n_structural_indexes - 1]]; - } +simdjson_inline bool json_iterator::at_eof() const noexcept { + return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; +} +simdjson_inline bool json_iterator::at_beginning() const noexcept { + return next_structural == dom_parser.structural_indexes.get(); +} +simdjson_inline uint8_t json_iterator::last_structural() const noexcept { + return buf[dom_parser.structural_indexes[dom_parser.n_structural_indexes - 1]]; +} - simdjson_inline void json_iterator::log_value(const char* type) const noexcept { - logger::log_line(*this, "", type, ""); - } +simdjson_inline void json_iterator::log_value(const char *type) const noexcept { + logger::log_line(*this, "", type, ""); +} - simdjson_inline void json_iterator::log_start_value(const char* type) const noexcept { - logger::log_line(*this, "+", type, ""); - if (logger::LOG_ENABLED) { logger::log_depth++; } - } +simdjson_inline void json_iterator::log_start_value(const char *type) const noexcept { + logger::log_line(*this, "+", type, ""); + if (logger::LOG_ENABLED) { logger::log_depth++; } +} - simdjson_inline void json_iterator::log_end_value(const char* type) const noexcept { - if (logger::LOG_ENABLED) { logger::log_depth--; } - logger::log_line(*this, "-", type, ""); - } +simdjson_inline void json_iterator::log_end_value(const char *type) const noexcept { + if (logger::LOG_ENABLED) { logger::log_depth--; } + logger::log_line(*this, "-", type, ""); +} - simdjson_inline void json_iterator::log_error(const char* error) const noexcept { - logger::log_line(*this, "", "ERROR", error); - } +simdjson_inline void json_iterator::log_error(const char *error) const noexcept { + logger::log_line(*this, "", "ERROR", error); +} - template - simdjson_warn_unused simdjson_inline error_code json_iterator::visit_root_primitive(V& visitor, const uint8_t* value) noexcept { - switch (*value) { - case '"': return visitor.visit_root_string(*this, value); - case 't': return visitor.visit_root_true_atom(*this, value); - case 'f': return visitor.visit_root_false_atom(*this, value); - case 'n': return visitor.visit_root_null_atom(*this, value); - case '-': - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - return visitor.visit_root_number(*this, value); - default: - log_error("Document starts with a non-value character"); - return TAPE_ERROR; - } - } - template - simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V& visitor, const uint8_t* value) noexcept { - // Use the fact that most scalars are going to be either strings or numbers. - if (*value == '"') { - return visitor.visit_string(*this, value); - } - else if (((*value - '0') < 10) || (*value == '-')) { - return visitor.visit_number(*this, value); - } - // true, false, null are uncommon. - switch (*value) { - case 't': return visitor.visit_true_atom(*this, value); - case 'f': return visitor.visit_false_atom(*this, value); - case 'n': return visitor.visit_null_atom(*this, value); - default: - log_error("Non-value found when value was expected!"); - return TAPE_ERROR; - } - } +template +simdjson_warn_unused simdjson_inline error_code json_iterator::visit_root_primitive(V &visitor, const uint8_t *value) noexcept { + switch (*value) { + case '"': return visitor.visit_root_string(*this, value); + case 't': return visitor.visit_root_true_atom(*this, value); + case 'f': return visitor.visit_root_false_atom(*this, value); + case 'n': return visitor.visit_root_null_atom(*this, value); + case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return visitor.visit_root_number(*this, value); + default: + log_error("Document starts with a non-value character"); + return TAPE_ERROR; + } +} +template +simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V &visitor, const uint8_t *value) noexcept { + // Use the fact that most scalars are going to be either strings or numbers. + if(*value == '"') { + return visitor.visit_string(*this, value); + } else if (((*value - '0') < 10) || (*value == '-')) { + return visitor.visit_number(*this, value); + } + // true, false, null are uncommon. + switch (*value) { + case 't': return visitor.visit_true_atom(*this, value); + case 'f': return visitor.visit_false_atom(*this, value); + case 'n': return visitor.visit_null_atom(*this, value); + default: + log_error("Non-value found when value was expected!"); + return TAPE_ERROR; + } +} - } // namespace stage2 - } // unnamed namespace - } // namespace ppc64 +} // namespace stage2 +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_JSON_ITERATOR_H /* end file generic/stage2/json_iterator.h for ppc64 */ /* including generic/stage2/stringparsing.h for ppc64: #include */ /* begin file generic/stage2/stringparsing.h for ppc64 */ +#include #ifndef SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -32376,242 +34045,238 @@ namespace simdjson { // It is intended to be included multiple times and compiled multiple times namespace simdjson { - namespace ppc64 { - namespace { - /// @private - namespace stringparsing { +namespace ppc64 { +namespace { +/// @private +namespace stringparsing { - // begin copypasta - // These chars yield themselves: " \ / - // b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab - // u not handled in this table as it's complex - static const uint8_t escape_map[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// begin copypasta +// These chars yield themselves: " \ / +// b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab +// u not handled in this table as it's complex +static const uint8_t escape_map[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0. + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5. - 0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6. - 0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7. + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4. + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5. + 0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6. + 0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - }; + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; - // handle a unicode codepoint - // write appropriate values into dest - // src will advance 6 bytes or 12 bytes - // dest will advance a variable amount (return via pointer) - // return true if the unicode codepoint was valid - // We work in little-endian then swap at write time - simdjson_warn_unused - simdjson_inline bool handle_unicode_codepoint(const uint8_t** src_ptr, - uint8_t** dst_ptr, bool allow_replacement) { - // Use the default Unicode Character 'REPLACEMENT CHARACTER' (U+FFFD) - constexpr uint32_t substitution_code_point = 0xfffd; - // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the - // conversion is not valid; we defer the check for this to inside the - // multilingual plane check. - uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); - *src_ptr += 6; +// handle a unicode codepoint +// write appropriate values into dest +// src will advance 6 bytes or 12 bytes +// dest will advance a variable amount (return via pointer) +// return true if the unicode codepoint was valid +// We work in little-endian then swap at write time +simdjson_warn_unused +simdjson_inline bool handle_unicode_codepoint(const uint8_t **src_ptr, + uint8_t **dst_ptr, bool allow_replacement) { + // Use the default Unicode Character 'REPLACEMENT CHARACTER' (U+FFFD) + constexpr uint32_t substitution_code_point = 0xfffd; + // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the + // conversion is not valid; we defer the check for this to inside the + // multilingual plane check. + uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); + *src_ptr += 6; - // If we found a high surrogate, we must - // check for low surrogate for characters - // outside the Basic - // Multilingual Plane. - if (code_point >= 0xd800 && code_point < 0xdc00) { - const uint8_t* src_data = *src_ptr; - /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ - if (((src_data[0] << 8) | src_data[1]) != ((static_cast ('\\') << 8) | static_cast ('u'))) { - if (!allow_replacement) { return false; } - code_point = substitution_code_point; - } - else { - uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); + // If we found a high surrogate, we must + // check for low surrogate for characters + // outside the Basic + // Multilingual Plane. + if (code_point >= 0xd800 && code_point < 0xdc00) { + const uint8_t *src_data = *src_ptr; + /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ + if (((src_data[0] << 8) | src_data[1]) != ((static_cast ('\\') << 8) | static_cast ('u'))) { + if(!allow_replacement) { return false; } + code_point = substitution_code_point; + } else { + uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); - // We have already checked that the high surrogate is valid and - // (code_point - 0xd800) < 1024. - // - // Check that code_point_2 is in the range 0xdc00..0xdfff - // and that code_point_2 was parsed from valid hex. - uint32_t low_bit = code_point_2 - 0xdc00; - if (low_bit >> 10) { - if (!allow_replacement) { return false; } - code_point = substitution_code_point; - } - else { - code_point = (((code_point - 0xd800) << 10) | low_bit) + 0x10000; - *src_ptr += 6; - } + // We have already checked that the high surrogate is valid and + // (code_point - 0xd800) < 1024. + // + // Check that code_point_2 is in the range 0xdc00..0xdfff + // and that code_point_2 was parsed from valid hex. + uint32_t low_bit = code_point_2 - 0xdc00; + if (low_bit >> 10) { + if(!allow_replacement) { return false; } + code_point = substitution_code_point; + } else { + code_point = (((code_point - 0xd800) << 10) | low_bit) + 0x10000; + *src_ptr += 6; + } - } - } - else if (code_point >= 0xdc00 && code_point <= 0xdfff) { - // If we encounter a low surrogate (not preceded by a high surrogate) - // then we have an error. - if (!allow_replacement) { return false; } - code_point = substitution_code_point; - } - size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); - *dst_ptr += offset; - return offset > 0; - } + } + } else if (code_point >= 0xdc00 && code_point <= 0xdfff) { + // If we encounter a low surrogate (not preceded by a high surrogate) + // then we have an error. + if(!allow_replacement) { return false; } + code_point = substitution_code_point; + } + size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); + *dst_ptr += offset; + return offset > 0; +} - // handle a unicode codepoint using the wobbly convention - // https://simonsapin.github.io/wtf-8/ - // write appropriate values into dest - // src will advance 6 bytes or 12 bytes - // dest will advance a variable amount (return via pointer) - // return true if the unicode codepoint was valid - // We work in little-endian then swap at write time - simdjson_warn_unused - simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t** src_ptr, - uint8_t** dst_ptr) { - // It is not ideal that this function is nearly identical to handle_unicode_codepoint. - // - // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the - // conversion is not valid; we defer the check for this to inside the - // multilingual plane check. - uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); - *src_ptr += 6; - // If we found a high surrogate, we must - // check for low surrogate for characters - // outside the Basic - // Multilingual Plane. - if (code_point >= 0xd800 && code_point < 0xdc00) { - const uint8_t* src_data = *src_ptr; - /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ - if (((src_data[0] << 8) | src_data[1]) == ((static_cast ('\\') << 8) | static_cast ('u'))) { - uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); - uint32_t low_bit = code_point_2 - 0xdc00; - if ((low_bit >> 10) == 0) { - code_point = - (((code_point - 0xd800) << 10) | low_bit) + 0x10000; - *src_ptr += 6; - } - } - } +// handle a unicode codepoint using the wobbly convention +// https://simonsapin.github.io/wtf-8/ +// write appropriate values into dest +// src will advance 6 bytes or 12 bytes +// dest will advance a variable amount (return via pointer) +// return true if the unicode codepoint was valid +// We work in little-endian then swap at write time +simdjson_warn_unused +simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t **src_ptr, + uint8_t **dst_ptr) { + // It is not ideal that this function is nearly identical to handle_unicode_codepoint. + // + // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the + // conversion is not valid; we defer the check for this to inside the + // multilingual plane check. + uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); + *src_ptr += 6; + // If we found a high surrogate, we must + // check for low surrogate for characters + // outside the Basic + // Multilingual Plane. + if (code_point >= 0xd800 && code_point < 0xdc00) { + const uint8_t *src_data = *src_ptr; + /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ + if (((src_data[0] << 8) | src_data[1]) == ((static_cast ('\\') << 8) | static_cast ('u'))) { + uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); + uint32_t low_bit = code_point_2 - 0xdc00; + if ((low_bit >> 10) == 0) { + code_point = + (((code_point - 0xd800) << 10) | low_bit) + 0x10000; + *src_ptr += 6; + } + } + } - size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); - *dst_ptr += offset; - return offset > 0; - } + size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); + *dst_ptr += offset; + return offset > 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. - */ - simdjson_warn_unused simdjson_inline uint8_t* parse_string(const uint8_t* src, uint8_t* dst, bool allow_replacement) { - while (1) { - // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); - // If the next thing is the end quote, copy and return - if (bs_quote.has_quote_first()) { - // we encountered quotes first. Move dst to point to quotes and exit - return dst + bs_quote.quote_index(); - } - if (bs_quote.has_backslash()) { - /* find out where the backspace is */ - auto bs_dist = bs_quote.backslash_index(); - uint8_t escape_char = src[bs_dist + 1]; - /* we encountered backslash first. Handle backslash */ - if (escape_char == 'u') { - /* move src/dst up to the start; they will be further adjusted - within the unicode codepoint handling code. */ - src += bs_dist; - dst += bs_dist; - if (!handle_unicode_codepoint(&src, &dst, allow_replacement)) { - return nullptr; - } - } - else { - /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and - * write bs_dist+1 characters to output - * note this may reach beyond the part of the buffer we've actually - * seen. I think this is ok */ - uint8_t escape_result = escape_map[escape_char]; - if (escape_result == 0u) { - return nullptr; /* bogus escape value is an error */ - } - dst[bs_dist] = escape_result; - src += bs_dist + 2; - dst += bs_dist + 1; - } - } - else { - /* they are the same. Since they can't co-occur, it means we - * encountered neither. */ - src += backslash_and_quote::BYTES_PROCESSED; - dst += backslash_and_quote::BYTES_PROCESSED; - } - } - } +/** + * 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. + */ +simdjson_warn_unused simdjson_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) { + while (1) { + // Copy the next n bytes, and find the backslash and quote in them. + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); + // If the next thing is the end quote, copy and return + if (bs_quote.has_quote_first()) { + // we encountered quotes first. Move dst to point to quotes and exit + return dst + bs_quote.quote_index(); + } + if (bs_quote.has_backslash()) { + /* find out where the backspace is */ + auto bs_dist = bs_quote.backslash_index(); + uint8_t escape_char = src[bs_dist + 1]; + /* we encountered backslash first. Handle backslash */ + if (escape_char == 'u') { + /* move src/dst up to the start; they will be further adjusted + within the unicode codepoint handling code. */ + src += bs_dist; + dst += bs_dist; + if (!handle_unicode_codepoint(&src, &dst, allow_replacement)) { + return nullptr; + } + } else { + /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and + * write bs_dist+1 characters to output + * note this may reach beyond the part of the buffer we've actually + * seen. I think this is ok */ + uint8_t escape_result = escape_map[escape_char]; + if (escape_result == 0u) { + return nullptr; /* bogus escape value is an error */ + } + dst[bs_dist] = escape_result; + src += bs_dist + 2; + dst += bs_dist + 1; + } + } else { + /* they are the same. Since they can't co-occur, it means we + * encountered neither. */ + src += backslash_and_quote::BYTES_PROCESSED; + dst += backslash_and_quote::BYTES_PROCESSED; + } + } +} - simdjson_warn_unused simdjson_inline uint8_t* parse_wobbly_string(const uint8_t* src, uint8_t* dst) { - // It is not ideal that this function is nearly identical to parse_string. - while (1) { - // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); - // If the next thing is the end quote, copy and return - if (bs_quote.has_quote_first()) { - // we encountered quotes first. Move dst to point to quotes and exit - return dst + bs_quote.quote_index(); - } - if (bs_quote.has_backslash()) { - /* find out where the backspace is */ - auto bs_dist = bs_quote.backslash_index(); - uint8_t escape_char = src[bs_dist + 1]; - /* we encountered backslash first. Handle backslash */ - if (escape_char == 'u') { - /* move src/dst up to the start; they will be further adjusted - within the unicode codepoint handling code. */ - src += bs_dist; - dst += bs_dist; - if (!handle_unicode_codepoint_wobbly(&src, &dst)) { - return nullptr; - } - } - else { - /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and - * write bs_dist+1 characters to output - * note this may reach beyond the part of the buffer we've actually - * seen. I think this is ok */ - uint8_t escape_result = escape_map[escape_char]; - if (escape_result == 0u) { - return nullptr; /* bogus escape value is an error */ - } - dst[bs_dist] = escape_result; - src += bs_dist + 2; - dst += bs_dist + 1; - } - } - else { - /* they are the same. Since they can't co-occur, it means we - * encountered neither. */ - src += backslash_and_quote::BYTES_PROCESSED; - dst += backslash_and_quote::BYTES_PROCESSED; - } - } - } +simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) { + // It is not ideal that this function is nearly identical to parse_string. + while (1) { + // Copy the next n bytes, and find the backslash and quote in them. + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); + // If the next thing is the end quote, copy and return + if (bs_quote.has_quote_first()) { + // we encountered quotes first. Move dst to point to quotes and exit + return dst + bs_quote.quote_index(); + } + if (bs_quote.has_backslash()) { + /* find out where the backspace is */ + auto bs_dist = bs_quote.backslash_index(); + uint8_t escape_char = src[bs_dist + 1]; + /* we encountered backslash first. Handle backslash */ + if (escape_char == 'u') { + /* move src/dst up to the start; they will be further adjusted + within the unicode codepoint handling code. */ + src += bs_dist; + dst += bs_dist; + if (!handle_unicode_codepoint_wobbly(&src, &dst)) { + return nullptr; + } + } else { + /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and + * write bs_dist+1 characters to output + * note this may reach beyond the part of the buffer we've actually + * seen. I think this is ok */ + uint8_t escape_result = escape_map[escape_char]; + if (escape_result == 0u) { + return nullptr; /* bogus escape value is an error */ + } + dst[bs_dist] = escape_result; + src += bs_dist + 2; + dst += bs_dist + 1; + } + } else { + /* they are the same. Since they can't co-occur, it means we + * encountered neither. */ + src += backslash_and_quote::BYTES_PROCESSED; + dst += backslash_and_quote::BYTES_PROCESSED; + } + } +} - } // namespace stringparsing - } // unnamed namespace - } // namespace ppc64 +} // namespace stringparsing + +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H @@ -32627,58 +34292,58 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { - namespace { - namespace stage2 { +namespace ppc64 { +namespace { +namespace stage2 { - class structural_iterator { - public: - const uint8_t* const buf; - uint32_t* next_structural; - dom_parser_implementation& dom_parser; +class structural_iterator { +public: + const uint8_t* const buf; + uint32_t *next_structural; + dom_parser_implementation &dom_parser; - // Start a structural - simdjson_inline structural_iterator(dom_parser_implementation& _dom_parser, size_t start_structural_index) - : buf{ _dom_parser.buf }, - next_structural{ &_dom_parser.structural_indexes[start_structural_index] }, - dom_parser{ _dom_parser } { - } - // Get the buffer position of the current structural character - simdjson_inline const uint8_t* current() { - return &buf[*(next_structural - 1)]; - } - // Get the current structural character - simdjson_inline char current_char() { - return buf[*(next_structural - 1)]; - } - // Get the next structural character without advancing - simdjson_inline char peek_next_char() { - return buf[*next_structural]; - } - simdjson_inline const uint8_t* peek() { - return &buf[*next_structural]; - } - simdjson_inline const uint8_t* advance() { - return &buf[*(next_structural++)]; - } - simdjson_inline char advance_char() { - return buf[*(next_structural++)]; - } - simdjson_inline size_t remaining_len() { - return dom_parser.len - *(next_structural - 1); - } + // Start a structural + simdjson_inline structural_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index) + : buf{_dom_parser.buf}, + next_structural{&_dom_parser.structural_indexes[start_structural_index]}, + dom_parser{_dom_parser} { + } + // Get the buffer position of the current structural character + simdjson_inline const uint8_t* current() { + return &buf[*(next_structural-1)]; + } + // Get the current structural character + simdjson_inline char current_char() { + return buf[*(next_structural-1)]; + } + // Get the next structural character without advancing + simdjson_inline char peek_next_char() { + return buf[*next_structural]; + } + simdjson_inline const uint8_t* peek() { + return &buf[*next_structural]; + } + simdjson_inline const uint8_t* advance() { + return &buf[*(next_structural++)]; + } + simdjson_inline char advance_char() { + return buf[*(next_structural++)]; + } + simdjson_inline size_t remaining_len() { + return dom_parser.len - *(next_structural-1); + } - simdjson_inline bool at_end() { - return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; - } - simdjson_inline bool at_beginning() { - return next_structural == dom_parser.structural_indexes.get(); - } - }; + simdjson_inline bool at_end() { + return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; + } + simdjson_inline bool at_beginning() { + return next_structural == dom_parser.structural_indexes.get(); + } +}; - } // namespace stage2 - } // unnamed namespace - } // namespace ppc64 +} // namespace stage2 +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_STRUCTURAL_ITERATOR_H @@ -32701,284 +34366,284 @@ namespace simdjson { namespace simdjson { - namespace ppc64 { - namespace { - namespace stage2 { +namespace ppc64 { +namespace { +namespace stage2 { - struct tape_builder { - template - simdjson_warn_unused static simdjson_inline error_code parse_document( - dom_parser_implementation& dom_parser, - dom::document& doc) noexcept; +struct tape_builder { + template + simdjson_warn_unused static simdjson_inline error_code parse_document( + dom_parser_implementation &dom_parser, + dom::document &doc) noexcept; - /** Called when a non-empty document starts. */ - simdjson_warn_unused simdjson_inline error_code visit_document_start(json_iterator& iter) noexcept; - /** Called when a non-empty document ends without error. */ - simdjson_warn_unused simdjson_inline error_code visit_document_end(json_iterator& iter) noexcept; + /** Called when a non-empty document starts. */ + simdjson_warn_unused simdjson_inline error_code visit_document_start(json_iterator &iter) noexcept; + /** Called when a non-empty document ends without error. */ + simdjson_warn_unused simdjson_inline error_code visit_document_end(json_iterator &iter) noexcept; - /** Called when a non-empty array starts. */ - simdjson_warn_unused simdjson_inline error_code visit_array_start(json_iterator& iter) noexcept; - /** Called when a non-empty array ends. */ - simdjson_warn_unused simdjson_inline error_code visit_array_end(json_iterator& iter) noexcept; - /** Called when an empty array is found. */ - simdjson_warn_unused simdjson_inline error_code visit_empty_array(json_iterator& iter) noexcept; + /** Called when a non-empty array starts. */ + simdjson_warn_unused simdjson_inline error_code visit_array_start(json_iterator &iter) noexcept; + /** Called when a non-empty array ends. */ + simdjson_warn_unused simdjson_inline error_code visit_array_end(json_iterator &iter) noexcept; + /** Called when an empty array is found. */ + simdjson_warn_unused simdjson_inline error_code visit_empty_array(json_iterator &iter) noexcept; - /** Called when a non-empty object starts. */ - simdjson_warn_unused simdjson_inline error_code visit_object_start(json_iterator& iter) noexcept; - /** - * Called when a key in a field is encountered. - * - * primitive, visit_object_start, visit_empty_object, visit_array_start, or visit_empty_array - * will be called after this with the field value. - */ - simdjson_warn_unused simdjson_inline error_code visit_key(json_iterator& iter, const uint8_t* key) noexcept; - /** Called when a non-empty object ends. */ - simdjson_warn_unused simdjson_inline error_code visit_object_end(json_iterator& iter) noexcept; - /** Called when an empty object is found. */ - simdjson_warn_unused simdjson_inline error_code visit_empty_object(json_iterator& iter) noexcept; + /** Called when a non-empty object starts. */ + simdjson_warn_unused simdjson_inline error_code visit_object_start(json_iterator &iter) noexcept; + /** + * Called when a key in a field is encountered. + * + * primitive, visit_object_start, visit_empty_object, visit_array_start, or visit_empty_array + * will be called after this with the field value. + */ + simdjson_warn_unused simdjson_inline error_code visit_key(json_iterator &iter, const uint8_t *key) noexcept; + /** Called when a non-empty object ends. */ + simdjson_warn_unused simdjson_inline error_code visit_object_end(json_iterator &iter) noexcept; + /** Called when an empty object is found. */ + simdjson_warn_unused simdjson_inline error_code visit_empty_object(json_iterator &iter) noexcept; - /** - * Called when a string, number, boolean or null is found. - */ - simdjson_warn_unused simdjson_inline error_code visit_primitive(json_iterator& iter, const uint8_t* value) noexcept; - /** - * Called when a string, number, boolean or null is found at the top level of a document (i.e. - * when there is no array or object and the entire document is a single string, number, boolean or - * null. - * - * This is separate from primitive() because simdjson's normal primitive parsing routines assume - * there is at least one more token after the value, which is only true in an array or object. - */ - simdjson_warn_unused simdjson_inline error_code visit_root_primitive(json_iterator& iter, const uint8_t* value) noexcept; + /** + * Called when a string, number, boolean or null is found. + */ + simdjson_warn_unused simdjson_inline error_code visit_primitive(json_iterator &iter, const uint8_t *value) noexcept; + /** + * Called when a string, number, boolean or null is found at the top level of a document (i.e. + * when there is no array or object and the entire document is a single string, number, boolean or + * null. + * + * This is separate from primitive() because simdjson's normal primitive parsing routines assume + * there is at least one more token after the value, which is only true in an array or object. + */ + simdjson_warn_unused simdjson_inline error_code visit_root_primitive(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_string(json_iterator& iter, const uint8_t* value, bool key = false) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_number(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_true_atom(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_false_atom(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_null_atom(json_iterator& iter, const uint8_t* value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_string(json_iterator &iter, const uint8_t *value, bool key = false) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_number(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_true_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_false_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_null_atom(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_string(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_number(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_true_atom(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_false_atom(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_null_atom(json_iterator& iter, const uint8_t* value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_string(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_number(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_true_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept; - /** Called each time a new field or element in an array or object is found. */ - simdjson_warn_unused simdjson_inline error_code increment_count(json_iterator& iter) noexcept; + /** Called each time a new field or element in an array or object is found. */ + simdjson_warn_unused simdjson_inline error_code increment_count(json_iterator &iter) noexcept; - /** Next location to write to tape */ - tape_writer tape; - private: - /** Next write location in the string buf for stage 2 parsing */ - uint8_t* current_string_buf_loc; + /** Next location to write to tape */ + tape_writer tape; +private: + /** Next write location in the string buf for stage 2 parsing */ + uint8_t *current_string_buf_loc; - simdjson_inline tape_builder(dom::document& doc) noexcept; + simdjson_inline tape_builder(dom::document &doc) noexcept; - simdjson_inline uint32_t next_tape_index(json_iterator& iter) const noexcept; - simdjson_inline void start_container(json_iterator& iter) noexcept; - simdjson_warn_unused simdjson_inline error_code end_container(json_iterator& iter, internal::tape_type start, internal::tape_type end) noexcept; - simdjson_warn_unused simdjson_inline error_code empty_container(json_iterator& iter, internal::tape_type start, internal::tape_type end) noexcept; - simdjson_inline uint8_t* on_start_string(json_iterator& iter) noexcept; - simdjson_inline void on_end_string(uint8_t* dst) noexcept; - }; // struct tape_builder + simdjson_inline uint32_t next_tape_index(json_iterator &iter) const noexcept; + simdjson_inline void start_container(json_iterator &iter) noexcept; + simdjson_warn_unused simdjson_inline error_code end_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept; + simdjson_warn_unused simdjson_inline error_code empty_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept; + simdjson_inline uint8_t *on_start_string(json_iterator &iter) noexcept; + simdjson_inline void on_end_string(uint8_t *dst) noexcept; +}; // struct tape_builder - template - simdjson_warn_unused simdjson_inline error_code tape_builder::parse_document( - dom_parser_implementation& dom_parser, - dom::document& doc) noexcept { - dom_parser.doc = &doc; - json_iterator iter(dom_parser, STREAMING ? dom_parser.next_structural_index : 0); - tape_builder builder(doc); - return iter.walk_document(builder); - } +template +simdjson_warn_unused simdjson_inline error_code tape_builder::parse_document( + dom_parser_implementation &dom_parser, + dom::document &doc) noexcept { + dom_parser.doc = &doc; + json_iterator iter(dom_parser, STREAMING ? dom_parser.next_structural_index : 0); + tape_builder builder(doc); + return iter.walk_document(builder); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_primitive(json_iterator& iter, const uint8_t* value) noexcept { - return iter.visit_root_primitive(*this, value); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_primitive(json_iterator& iter, const uint8_t* value) noexcept { - return iter.visit_primitive(*this, value); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_object(json_iterator& iter) noexcept { - return empty_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_array(json_iterator& iter) noexcept { - return empty_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_primitive(json_iterator &iter, const uint8_t *value) noexcept { + return iter.visit_root_primitive(*this, value); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_primitive(json_iterator &iter, const uint8_t *value) noexcept { + return iter.visit_primitive(*this, value); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_object(json_iterator &iter) noexcept { + return empty_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_array(json_iterator &iter) noexcept { + return empty_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_start(json_iterator& iter) noexcept { - start_container(iter); - return SUCCESS; - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_start(json_iterator& iter) noexcept { - start_container(iter); - return SUCCESS; - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_start(json_iterator& iter) noexcept { - start_container(iter); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_start(json_iterator &iter) noexcept { + start_container(iter); + return SUCCESS; +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_start(json_iterator &iter) noexcept { + start_container(iter); + return SUCCESS; +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_start(json_iterator &iter) noexcept { + start_container(iter); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_end(json_iterator& iter) noexcept { - return end_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_end(json_iterator& iter) noexcept { - return end_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_end(json_iterator& iter) noexcept { - constexpr uint32_t start_tape_index = 0; - tape.append(start_tape_index, internal::tape_type::ROOT); - tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter), internal::tape_type::ROOT); - return SUCCESS; - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_key(json_iterator& iter, const uint8_t* key) noexcept { - return visit_string(iter, key, true); - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_end(json_iterator &iter) noexcept { + return end_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_end(json_iterator &iter) noexcept { + return end_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_end(json_iterator &iter) noexcept { + constexpr uint32_t start_tape_index = 0; + tape.append(start_tape_index, internal::tape_type::ROOT); + tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter), internal::tape_type::ROOT); + return SUCCESS; +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_key(json_iterator &iter, const uint8_t *key) noexcept { + return visit_string(iter, key, true); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::increment_count(json_iterator& iter) noexcept { - iter.dom_parser.open_containers[iter.depth].count++; // we have a key value pair in the object at parser.dom_parser.depth - 1 - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::increment_count(json_iterator &iter) noexcept { + iter.dom_parser.open_containers[iter.depth].count++; // we have a key value pair in the object at parser.dom_parser.depth - 1 + return SUCCESS; +} - simdjson_inline tape_builder::tape_builder(dom::document& doc) noexcept : tape{ doc.tape.get() }, current_string_buf_loc{ doc.string_buf.get() } {} +simdjson_inline tape_builder::tape_builder(dom::document &doc) noexcept : tape{doc.tape.get()}, current_string_buf_loc{doc.string_buf.get()} {} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_string(json_iterator& iter, const uint8_t* value, bool key) noexcept { - iter.log_value(key ? "key" : "string"); - uint8_t* dst = on_start_string(iter); - dst = stringparsing::parse_string(value + 1, dst, false); // We do not allow replacement when the escape characters are invalid. - if (dst == nullptr) { - iter.log_error("Invalid escape in string"); - return STRING_ERROR; - } - on_end_string(dst); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_string(json_iterator &iter, const uint8_t *value, bool key) noexcept { + iter.log_value(key ? "key" : "string"); + uint8_t *dst = on_start_string(iter); + dst = stringparsing::parse_string(value+1, dst, false); // We do not allow replacement when the escape characters are invalid. + if (dst == nullptr) { + iter.log_error("Invalid escape in string"); + return STRING_ERROR; + } + on_end_string(dst); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_string(json_iterator& iter, const uint8_t* value) noexcept { - return visit_string(iter, value); - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_string(json_iterator &iter, const uint8_t *value) noexcept { + return visit_string(iter, value); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_number(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("number"); - return numberparsing::parse_number(value, tape); - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_number(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("number"); + return numberparsing::parse_number(value, tape); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_number(json_iterator& iter, const uint8_t* value) noexcept { - // - // We need to make a copy to make sure that the string is space terminated. - // This is not about padding the input, which should already padded up - // to len + SIMDJSON_PADDING. However, we have no control at this stage - // on how the padding was done. What if the input string was padded with nulls? - // It is quite common for an input string to have an extra null character (C string). - // We do not want to allow 9\0 (where \0 is the null character) inside a JSON - // document, but the string "9\0" by itself is fine. So we make a copy and - // pad the input with spaces when we know that there is just one input element. - // This copy is relatively expensive, but it will almost never be called in - // practice unless you are in the strange scenario where you have many JSON - // documents made of single atoms. - // - std::unique_ptrcopy(new (std::nothrow) uint8_t[iter.remaining_len() + SIMDJSON_PADDING]); - if (copy.get() == nullptr) { return MEMALLOC; } - std::memcpy(copy.get(), value, iter.remaining_len()); - std::memset(copy.get() + iter.remaining_len(), ' ', SIMDJSON_PADDING); - error_code error = visit_number(iter, copy.get()); - return error; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_number(json_iterator &iter, const uint8_t *value) noexcept { + // + // We need to make a copy to make sure that the string is space terminated. + // This is not about padding the input, which should already padded up + // to len + SIMDJSON_PADDING. However, we have no control at this stage + // on how the padding was done. What if the input string was padded with nulls? + // It is quite common for an input string to have an extra null character (C string). + // We do not want to allow 9\0 (where \0 is the null character) inside a JSON + // document, but the string "9\0" by itself is fine. So we make a copy and + // pad the input with spaces when we know that there is just one input element. + // This copy is relatively expensive, but it will almost never be called in + // practice unless you are in the strange scenario where you have many JSON + // documents made of single atoms. + // + std::unique_ptrcopy(new (std::nothrow) uint8_t[iter.remaining_len() + SIMDJSON_PADDING]); + if (copy.get() == nullptr) { return MEMALLOC; } + std::memcpy(copy.get(), value, iter.remaining_len()); + std::memset(copy.get() + iter.remaining_len(), ' ', SIMDJSON_PADDING); + error_code error = visit_number(iter, copy.get()); + return error; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_true_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("true"); - if (!atomparsing::is_valid_true_atom(value)) { return T_ATOM_ERROR; } - tape.append(0, internal::tape_type::TRUE_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_true_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("true"); + if (!atomparsing::is_valid_true_atom(value)) { return T_ATOM_ERROR; } + tape.append(0, internal::tape_type::TRUE_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_true_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("true"); - if (!atomparsing::is_valid_true_atom(value, iter.remaining_len())) { return T_ATOM_ERROR; } - tape.append(0, internal::tape_type::TRUE_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_true_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("true"); + if (!atomparsing::is_valid_true_atom(value, iter.remaining_len())) { return T_ATOM_ERROR; } + tape.append(0, internal::tape_type::TRUE_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_false_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("false"); - if (!atomparsing::is_valid_false_atom(value)) { return F_ATOM_ERROR; } - tape.append(0, internal::tape_type::FALSE_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_false_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("false"); + if (!atomparsing::is_valid_false_atom(value)) { return F_ATOM_ERROR; } + tape.append(0, internal::tape_type::FALSE_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_false_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("false"); - if (!atomparsing::is_valid_false_atom(value, iter.remaining_len())) { return F_ATOM_ERROR; } - tape.append(0, internal::tape_type::FALSE_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("false"); + if (!atomparsing::is_valid_false_atom(value, iter.remaining_len())) { return F_ATOM_ERROR; } + tape.append(0, internal::tape_type::FALSE_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_null_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("null"); - if (!atomparsing::is_valid_null_atom(value)) { return N_ATOM_ERROR; } - tape.append(0, internal::tape_type::NULL_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_null_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("null"); + if (!atomparsing::is_valid_null_atom(value)) { return N_ATOM_ERROR; } + tape.append(0, internal::tape_type::NULL_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_null_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("null"); - if (!atomparsing::is_valid_null_atom(value, iter.remaining_len())) { return N_ATOM_ERROR; } - tape.append(0, internal::tape_type::NULL_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("null"); + if (!atomparsing::is_valid_null_atom(value, iter.remaining_len())) { return N_ATOM_ERROR; } + tape.append(0, internal::tape_type::NULL_VALUE); + return SUCCESS; +} - // private: +// private: - simdjson_inline uint32_t tape_builder::next_tape_index(json_iterator& iter) const noexcept { - return uint32_t(tape.next_tape_loc - iter.dom_parser.doc->tape.get()); - } +simdjson_inline uint32_t tape_builder::next_tape_index(json_iterator &iter) const noexcept { + return uint32_t(tape.next_tape_loc - iter.dom_parser.doc->tape.get()); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::empty_container(json_iterator& iter, internal::tape_type start, internal::tape_type end) noexcept { - auto start_index = next_tape_index(iter); - tape.append(start_index + 2, start); - tape.append(start_index, end); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::empty_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept { + auto start_index = next_tape_index(iter); + tape.append(start_index+2, start); + tape.append(start_index, end); + return SUCCESS; +} - simdjson_inline void tape_builder::start_container(json_iterator& iter) noexcept { - iter.dom_parser.open_containers[iter.depth].tape_index = next_tape_index(iter); - iter.dom_parser.open_containers[iter.depth].count = 0; - tape.skip(); // We don't actually *write* the start element until the end. - } +simdjson_inline void tape_builder::start_container(json_iterator &iter) noexcept { + iter.dom_parser.open_containers[iter.depth].tape_index = next_tape_index(iter); + iter.dom_parser.open_containers[iter.depth].count = 0; + tape.skip(); // We don't actually *write* the start element until the end. +} - simdjson_warn_unused simdjson_inline error_code tape_builder::end_container(json_iterator& iter, internal::tape_type start, internal::tape_type end) noexcept { - // Write the ending tape element, pointing at the start location - const uint32_t start_tape_index = iter.dom_parser.open_containers[iter.depth].tape_index; - tape.append(start_tape_index, end); - // Write the start tape element, pointing at the end location (and including count) - // count can overflow if it exceeds 24 bits... so we saturate - // the convention being that a cnt of 0xffffff or more is undetermined in value (>= 0xffffff). - const uint32_t count = iter.dom_parser.open_containers[iter.depth].count; - const uint32_t cntsat = count > 0xFFFFFF ? 0xFFFFFF : count; - tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter) | (uint64_t(cntsat) << 32), start); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::end_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept { + // Write the ending tape element, pointing at the start location + const uint32_t start_tape_index = iter.dom_parser.open_containers[iter.depth].tape_index; + tape.append(start_tape_index, end); + // Write the start tape element, pointing at the end location (and including count) + // count can overflow if it exceeds 24 bits... so we saturate + // the convention being that a cnt of 0xffffff or more is undetermined in value (>= 0xffffff). + const uint32_t count = iter.dom_parser.open_containers[iter.depth].count; + const uint32_t cntsat = count > 0xFFFFFF ? 0xFFFFFF : count; + tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter) | (uint64_t(cntsat) << 32), start); + return SUCCESS; +} - simdjson_inline uint8_t* tape_builder::on_start_string(json_iterator& iter) noexcept { - // we advance the point, accounting for the fact that we have a NULL termination - tape.append(current_string_buf_loc - iter.dom_parser.doc->string_buf.get(), internal::tape_type::STRING); - return current_string_buf_loc + sizeof(uint32_t); - } +simdjson_inline uint8_t *tape_builder::on_start_string(json_iterator &iter) noexcept { + // we advance the point, accounting for the fact that we have a NULL termination + tape.append(current_string_buf_loc - iter.dom_parser.doc->string_buf.get(), internal::tape_type::STRING); + return current_string_buf_loc + sizeof(uint32_t); +} - simdjson_inline void tape_builder::on_end_string(uint8_t* dst) noexcept { - uint32_t str_length = uint32_t(dst - (current_string_buf_loc + sizeof(uint32_t))); - // TODO check for overflow in case someone has a crazy string (>=4GB?) - // But only add the overflow check when the document itself exceeds 4GB - // Currently unneeded because we refuse to parse docs larger or equal to 4GB. - memcpy(current_string_buf_loc, &str_length, sizeof(uint32_t)); - // NULL termination is still handy if you expect all your strings to - // be NULL terminated? It comes at a small cost - *dst = 0; - current_string_buf_loc = dst + 1; - } +simdjson_inline void tape_builder::on_end_string(uint8_t *dst) noexcept { + uint32_t str_length = uint32_t(dst - (current_string_buf_loc + sizeof(uint32_t))); + // TODO check for overflow in case someone has a crazy string (>=4GB?) + // But only add the overflow check when the document itself exceeds 4GB + // Currently unneeded because we refuse to parse docs larger or equal to 4GB. + memcpy(current_string_buf_loc, &str_length, sizeof(uint32_t)); + // NULL termination is still handy if you expect all your strings to + // be NULL terminated? It comes at a small cost + *dst = 0; + current_string_buf_loc = dst + 1; +} - } // namespace stage2 - } // unnamed namespace - } // namespace ppc64 +} // namespace stage2 +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_BUILDER_H @@ -32989,75 +34654,75 @@ namespace simdjson { // Stage 1 // namespace simdjson { - namespace ppc64 { +namespace ppc64 { - simdjson_warn_unused error_code implementation::create_dom_parser_implementation( - size_t capacity, - size_t max_depth, - std::unique_ptr& dst - ) const noexcept { - dst.reset(new (std::nothrow) dom_parser_implementation()); - if (!dst) { return MEMALLOC; } - if (auto err = dst->set_capacity(capacity)) - return err; - if (auto err = dst->set_max_depth(max_depth)) - return err; - return SUCCESS; - } +simdjson_warn_unused error_code implementation::create_dom_parser_implementation( + size_t capacity, + size_t max_depth, + std::unique_ptr& dst +) const noexcept { + dst.reset( new (std::nothrow) dom_parser_implementation() ); + if (!dst) { return MEMALLOC; } + if (auto err = dst->set_capacity(capacity)) + return err; + if (auto err = dst->set_max_depth(max_depth)) + return err; + return SUCCESS; +} - namespace { +namespace { - using namespace simd; +using namespace simd; - simdjson_inline json_character_block json_character_block::classify(const simd::simd8x64& in) { - const simd8 table1(16, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 1, 2, 9, 0, 0); - const simd8 table2(8, 0, 18, 4, 0, 1, 0, 1, 0, 0, 0, 3, 2, 1, 0, 0); +simdjson_inline json_character_block json_character_block::classify(const simd::simd8x64& in) { + const simd8 table1(16, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 1, 2, 9, 0, 0); + const simd8 table2(8, 0, 18, 4, 0, 1, 0, 1, 0, 0, 0, 3, 2, 1, 0, 0); - simd8x64 v( - (in.chunks[0] & 0xf).lookup_16(table1) & (in.chunks[0].shr<4>()).lookup_16(table2), - (in.chunks[1] & 0xf).lookup_16(table1) & (in.chunks[1].shr<4>()).lookup_16(table2), - (in.chunks[2] & 0xf).lookup_16(table1) & (in.chunks[2].shr<4>()).lookup_16(table2), - (in.chunks[3] & 0xf).lookup_16(table1) & (in.chunks[3].shr<4>()).lookup_16(table2) - ); + simd8x64 v( + (in.chunks[0] & 0xf).lookup_16(table1) & (in.chunks[0].shr<4>()).lookup_16(table2), + (in.chunks[1] & 0xf).lookup_16(table1) & (in.chunks[1].shr<4>()).lookup_16(table2), + (in.chunks[2] & 0xf).lookup_16(table1) & (in.chunks[2].shr<4>()).lookup_16(table2), + (in.chunks[3] & 0xf).lookup_16(table1) & (in.chunks[3].shr<4>()).lookup_16(table2) + ); - uint64_t op = simd8x64( - v.chunks[0].any_bits_set(0x7), - v.chunks[1].any_bits_set(0x7), - v.chunks[2].any_bits_set(0x7), - v.chunks[3].any_bits_set(0x7) - ).to_bitmask(); + uint64_t op = simd8x64( + v.chunks[0].any_bits_set(0x7), + v.chunks[1].any_bits_set(0x7), + v.chunks[2].any_bits_set(0x7), + v.chunks[3].any_bits_set(0x7) + ).to_bitmask(); - uint64_t whitespace = simd8x64( - v.chunks[0].any_bits_set(0x18), - v.chunks[1].any_bits_set(0x18), - v.chunks[2].any_bits_set(0x18), - v.chunks[3].any_bits_set(0x18) - ).to_bitmask(); + uint64_t whitespace = simd8x64( + v.chunks[0].any_bits_set(0x18), + v.chunks[1].any_bits_set(0x18), + v.chunks[2].any_bits_set(0x18), + v.chunks[3].any_bits_set(0x18) + ).to_bitmask(); - return { whitespace, op }; - } + return { whitespace, op }; +} - simdjson_inline bool is_ascii(const simd8x64& input) { - // careful: 0x80 is not ascii. - return input.reduce_or().saturating_sub(0x7fu).bits_not_set_anywhere(); - } +simdjson_inline bool is_ascii(const simd8x64& input) { + // careful: 0x80 is not ascii. + return input.reduce_or().saturating_sub(0x7fu).bits_not_set_anywhere(); +} - simdjson_unused simdjson_inline simd8 must_be_continuation(const simd8 prev1, const simd8 prev2, const simd8 prev3) { - simd8 is_second_byte = prev1.saturating_sub(0xc0u - 1); // Only 11______ will be > 0 - simd8 is_third_byte = prev2.saturating_sub(0xe0u - 1); // Only 111_____ will be > 0 - simd8 is_fourth_byte = prev3.saturating_sub(0xf0u - 1); // Only 1111____ will be > 0 - // Caller requires a bool (all 1's). All values resulting from the subtraction will be <= 64, so signed comparison is fine. - return simd8(is_second_byte | is_third_byte | is_fourth_byte) > int8_t(0); - } +simdjson_unused simdjson_inline simd8 must_be_continuation(const simd8 prev1, const simd8 prev2, const simd8 prev3) { + simd8 is_second_byte = prev1.saturating_sub(0xc0u-1); // Only 11______ will be > 0 + simd8 is_third_byte = prev2.saturating_sub(0xe0u-1); // Only 111_____ will be > 0 + simd8 is_fourth_byte = prev3.saturating_sub(0xf0u-1); // Only 1111____ will be > 0 + // Caller requires a bool (all 1's). All values resulting from the subtraction will be <= 64, so signed comparison is fine. + return simd8(is_second_byte | is_third_byte | is_fourth_byte) > int8_t(0); +} - simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { - simd8 is_third_byte = prev2.saturating_sub(0xe0u - 0x80); // Only 111_____ will be >= 0x80 - simd8 is_fourth_byte = prev3.saturating_sub(0xf0u - 0x80); // Only 1111____ will be >= 0x80 - return is_third_byte | is_fourth_byte; - } +simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { + simd8 is_third_byte = prev2.saturating_sub(0xe0u-0x80); // Only 111_____ will be >= 0x80 + simd8 is_fourth_byte = prev3.saturating_sub(0xf0u-0x80); // Only 1111____ will be >= 0x80 + return is_third_byte | is_fourth_byte; +} - } // unnamed namespace - } // namespace ppc64 +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson // @@ -33068,45 +34733,46 @@ namespace simdjson { // Implementation-specific overrides // namespace simdjson { - namespace ppc64 { +namespace ppc64 { - simdjson_warn_unused error_code implementation::minify(const uint8_t* buf, size_t len, uint8_t* dst, size_t& dst_len) const noexcept { - return ppc64::stage1::json_minifier::minify<64>(buf, len, dst, dst_len); - } +simdjson_warn_unused error_code implementation::minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept { + return ppc64::stage1::json_minifier::minify<64>(buf, len, dst, dst_len); +} - simdjson_warn_unused error_code dom_parser_implementation::stage1(const uint8_t* _buf, size_t _len, stage1_mode streaming) noexcept { - this->buf = _buf; - this->len = _len; - return ppc64::stage1::json_structural_indexer::index<64>(buf, len, *this, streaming); - } +simdjson_warn_unused error_code dom_parser_implementation::stage1(const uint8_t *_buf, size_t _len, stage1_mode streaming) noexcept { + this->buf = _buf; + this->len = _len; + return ppc64::stage1::json_structural_indexer::index<64>(buf, len, *this, streaming); +} - simdjson_warn_unused bool implementation::validate_utf8(const char* buf, size_t len) const noexcept { - return ppc64::stage1::generic_validate_utf8(buf, len); - } +simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept { + return ppc64::stage1::generic_validate_utf8(buf,len); +} - simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document& _doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); - } +simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept { + return stage2::tape_builder::parse_document(*this, _doc); +} - simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document& _doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); - } +simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept { + return stage2::tape_builder::parse_document(*this, _doc); +} - simdjson_warn_unused uint8_t* dom_parser_implementation::parse_string(const uint8_t* src, uint8_t* dst, bool replacement_char) const noexcept { - return ppc64::stringparsing::parse_string(src, dst, replacement_char); - } +SIMDJSON_NO_SANITIZE_MEMORY +simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool replacement_char) const noexcept { + return ppc64::stringparsing::parse_string(src, dst, replacement_char); +} - simdjson_warn_unused uint8_t* dom_parser_implementation::parse_wobbly_string(const uint8_t* src, uint8_t* dst) const noexcept { - return ppc64::stringparsing::parse_wobbly_string(src, dst); - } +simdjson_warn_unused uint8_t *dom_parser_implementation::parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept { + return ppc64::stringparsing::parse_wobbly_string(src, dst); +} - simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t* _buf, size_t _len, dom::document& _doc) noexcept { - auto error = stage1(_buf, _len, stage1_mode::regular); - if (error) { return error; } - return stage2(_doc); - } +simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept { + auto error = stage1(_buf, _len, stage1_mode::regular); + if (error) { return error; } + return stage2(_doc); +} - } // namespace ppc64 +} // namespace ppc64 } // namespace simdjson /* including simdjson/ppc64/end.h: #include */ @@ -33153,23 +34819,23 @@ namespace simdjson { // 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 { +/** + * Implementation for Westmere (Intel SSE4.2). + */ +namespace westmere { - class implementation; +class implementation; - namespace { - namespace simd { +namespace { +namespace simd { - template struct simd8; - template struct simd8x64; +template struct simd8; +template struct simd8x64; - } // namespace simd - } // unnamed namespace +} // namespace simd +} // unnamed namespace - } // namespace westmere +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_WESTMERE_BASE_H @@ -33224,73 +34890,73 @@ SIMDJSON_TARGET_REGION("sse4.2,pclmul,popcnt") /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { +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) { +// 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; + 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); + 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 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) { +/* 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; + 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); + 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 - } +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); - } +simdjson_inline long long int count_ones(uint64_t input_num) { + return _popcnt64(input_num); +} #endif - simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, - uint64_t* result) { +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { #if SIMDJSON_REGULAR_VISUAL_STUDIO - return _addcarry_u64(0, value1, value2, - reinterpret_cast(result)); + return _addcarry_u64(0, value1, value2, + reinterpret_cast(result)); #else - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); #endif - } +} - } // unnamed namespace - } // namespace westmere +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_WESTMERE_BITMANIPULATION_H @@ -33306,24 +34972,24 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { +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); - } +// +// 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 +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_WESTMERE_BITMASK_H @@ -33344,23 +35010,23 @@ namespace simdjson { // 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 { +/** + * Implementation for Westmere (Intel SSE4.2). + */ +namespace westmere { - class implementation; +class implementation; - namespace { - namespace simd { +namespace { +namespace simd { - template struct simd8; - template struct simd8x64; +template struct simd8; +template struct simd8x64; - } // namespace simd - } // unnamed namespace +} // namespace simd +} // unnamed namespace - } // namespace westmere +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_WESTMERE_BASE_H @@ -33405,49 +35071,49 @@ static_assert(sizeof(__m128i) <= simdjson::SIMDJSON_PADDING, "insufficient paddi /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace numberparsing { +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(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 */ +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(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; +/** @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; + // 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 + 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); + __uint128_t r = (static_cast<__uint128_t>(value1)) * value2; + answer.low = uint64_t(r); + answer.high = uint64_t(r >> 64); #endif - return answer; - } + return answer; +} - } // namespace numberparsing - } // namespace westmere +} // namespace numberparsing +} // namespace westmere } // namespace simdjson #define SIMDJSON_SWAR_NUMBER_PARSING 1 @@ -33466,331 +35132,332 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { - namespace simd { +namespace westmere { +namespace { +namespace simd { - template - struct base { - __m128i value; + template + struct base { + __m128i value; - // Zero constructor - simdjson_inline base() : value{ __m128i() } {} + // Zero constructor + simdjson_inline base() : value{__m128i()} {} - // Conversion from SIMD register - simdjson_inline base(const __m128i _value) : value(_value) {} + // 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; } + // 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(this); *this_cast = *this_cast | other; return *this_cast; } - simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } - simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } - }; + // 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(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; - template> - struct base8 : base> { - typedef uint16_t bitmask_t; - typedef uint32_t bitmask2_t; + template> + struct base8: base> { + typedef uint16_t bitmask_t; + typedef uint32_t bitmask2_t; - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m128i _value) : base>(_value) {} + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m128i _value) : base>(_value) {} - friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm_cmpeq_epi8(lhs, rhs); } + friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm_cmpeq_epi8(lhs, rhs); } - static const int SIZE = sizeof(base>::value); + static const int SIZE = sizeof(base>::value); - template - simdjson_inline simd8 prev(const simd8 prev_chunk) const { - return _mm_alignr_epi8(*this, prev_chunk, 16 - N); - } - }; + template + simdjson_inline simd8 prev(const simd8 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 : base8 { - static simdjson_inline simd8 splat(bool _value) { return _mm_set1_epi8(uint8_t(-(!!_value))); } + // SIMD byte mask type (returned by things like eq and gt) + template<> + struct simd8: base8 { + static simdjson_inline simd8 splat(bool _value) { return _mm_set1_epi8(uint8_t(-(!!_value))); } - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m128i _value) : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) : base8(splat(_value)) {} + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m128i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(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 operator~() const { return *this ^ true; } - }; + 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 operator~() const { return *this ^ true; } + }; - template - struct base8_numeric : base8 { - static simdjson_inline simd8 splat(T _value) { return _mm_set1_epi8(_value); } - static simdjson_inline simd8 zero() { return _mm_setzero_si128(); } - static simdjson_inline simd8 load(const T values[16]) { - return _mm_loadu_si128(reinterpret_cast(values)); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdjson_inline simd8 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + template + struct base8_numeric: base8 { + static simdjson_inline simd8 splat(T _value) { return _mm_set1_epi8(_value); } + static simdjson_inline simd8 zero() { return _mm_setzero_si128(); } + static simdjson_inline simd8 load(const T values[16]) { + return _mm_loadu_si128(reinterpret_cast(values)); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - simdjson_inline base8_numeric() : base8() {} - simdjson_inline base8_numeric(const __m128i _value) : base8(_value) {} + simdjson_inline base8_numeric() : base8() {} + simdjson_inline base8_numeric(const __m128i _value) : base8(_value) {} - // Store to array - simdjson_inline void store(T dst[16]) const { return _mm_storeu_si128(reinterpret_cast<__m128i*>(dst), *this); } + // 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 operator~() const { return *this ^ 0xFFu; } + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - // Addition/subtraction are the same for signed and unsigned - simdjson_inline simd8 operator+(const simd8 other) const { return _mm_add_epi8(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return _mm_sub_epi8(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { return _mm_add_epi8(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return _mm_sub_epi8(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return _mm_shuffle_epi8(lookup_table, *this); - } + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 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 compress(uint32_t mask) would be - // sensible, but the AVX ISA makes this kind of approach difficult. - template - 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(pshufb_combine_table + pop1 * 8)); - __m128i answer = _mm_shuffle_epi8(pruned, compactmask); - _mm_storeu_si128(reinterpret_cast<__m128i*>(output), answer); - } + // 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 compress(uint32_t mask) would be + // sensible, but the AVX ISA makes this kind of approach difficult. + template + 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(pshufb_combine_table + pop1 * 8)); + __m128i answer = _mm_shuffle_epi8(pruned, compactmask); + _mm_storeu_si128(reinterpret_cast<__m128i *>(output), answer); + } - template - simdjson_inline simd8 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::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - }; + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; - // Signed bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + // Signed bytes + template<> + struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - // Order-sensitive comparisons - simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epi8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epi8(*this, other); } - simdjson_inline simd8 operator>(const simd8 other) const { return _mm_cmpgt_epi8(*this, other); } - simdjson_inline simd8 operator<(const simd8 other) const { return _mm_cmpgt_epi8(other, *this); } - }; + // Order-sensitive comparisons + simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epi8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epi8(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return _mm_cmpgt_epi8(*this, other); } + simdjson_inline simd8 operator<(const simd8 other) const { return _mm_cmpgt_epi8(other, *this); } + }; - // Unsigned bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + // Unsigned bytes + template<> + struct simd8: base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - // Saturated math - simdjson_inline simd8 saturating_add(const simd8 other) const { return _mm_adds_epu8(*this, other); } - simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm_subs_epu8(*this, other); } + // Saturated math + simdjson_inline simd8 saturating_add(const simd8 other) const { return _mm_adds_epu8(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm_subs_epu8(*this, other); } - // Order-specific operations - simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epu8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epu8(*this, other); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } - simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } - simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } - simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - simdjson_inline simd8 operator<(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + // Order-specific operations + simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epu8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epu8(*this, other); } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } + simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } + simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } + simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + simdjson_inline simd8 operator<(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - // Bit-specific operations - simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } - simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } - simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } - simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return _mm_testz_si128(*this, bits); } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } - template - simdjson_inline simd8 shr() const { return simd8(_mm_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } - template - simdjson_inline simd8 shl() const { return simd8(_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 - simdjson_inline int get_bit() const { return _mm_movemask_epi8(_mm_slli_epi16(*this, 7 - N)); } - }; + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } + simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } + simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } + simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return _mm_testz_si128(*this, bits); } + simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } + template + simdjson_inline simd8 shr() const { return simd8(_mm_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } + template + simdjson_inline simd8 shl() const { return simd8(_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 + simdjson_inline int get_bit() const { return _mm_movemask_epi8(_mm_slli_epi16(*this, 7-N)); } + }; - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 4, "Westmere kernel should use four registers per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 4, "Westmere kernel should use four registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{ chunk0, chunk1, chunk2, chunk3 } {} - simdjson_inline simd8x64(const T ptr[64]) : chunks{ simd8::load(ptr), simd8::load(ptr + 16), simd8::load(ptr + 32), simd8::load(ptr + 48) } {} + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::load(ptr+16), simd8::load(ptr+32), simd8::load(ptr+48)} {} - simdjson_inline void store(T ptr[64]) const { - this->chunks[0].store(ptr + sizeof(simd8) * 0); - this->chunks[1].store(ptr + sizeof(simd8) * 1); - this->chunks[2].store(ptr + sizeof(simd8) * 2); - this->chunks[3].store(ptr + sizeof(simd8) * 3); - } + simdjson_inline void store(T ptr[64]) const { + this->chunks[0].store(ptr+sizeof(simd8)*0); + this->chunks[1].store(ptr+sizeof(simd8)*1); + this->chunks[2].store(ptr+sizeof(simd8)*2); + this->chunks[3].store(ptr+sizeof(simd8)*3); + } - simdjson_inline simd8 reduce_or() const { - return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]); - } + simdjson_inline simd8 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 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 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] == mask, - this->chunks[1] == mask, - this->chunks[2] == mask, - this->chunks[3] == mask - ).to_bitmask(); - } + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + 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& other) const { - return simd8x64( - 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 eq(const simd8x64 &other) const { + return simd8x64( + 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] <= mask, - this->chunks[1] <= mask, - this->chunks[2] <= mask, - this->chunks[3] <= mask - ).to_bitmask(); - } - }; // struct simd8x64 + simdjson_inline uint64_t lteq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] <= mask, + this->chunks[1] <= mask, + this->chunks[2] <= mask, + this->chunks[3] <= mask + ).to_bitmask(); + } + }; // struct simd8x64 - } // namespace simd - } // unnamed namespace - } // namespace westmere +} // namespace simd +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_WESTMERE_SIMD_INPUT_H @@ -33811,73 +35478,73 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { +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) { +// 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; + 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); + 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 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) { +/* 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; + 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); + 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 - } +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); - } +simdjson_inline long long int count_ones(uint64_t input_num) { + return _popcnt64(input_num); +} #endif - simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, - uint64_t* result) { +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { #if SIMDJSON_REGULAR_VISUAL_STUDIO - return _addcarry_u64(0, value1, value2, - reinterpret_cast(result)); + return _addcarry_u64(0, value1, value2, + reinterpret_cast(result)); #else - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); #endif - } +} - } // unnamed namespace - } // namespace westmere +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_WESTMERE_BITMANIPULATION_H @@ -33894,374 +35561,400 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { - namespace simd { +namespace westmere { +namespace { +namespace simd { - template - struct base { - __m128i value; + template + struct base { + __m128i value; - // Zero constructor - simdjson_inline base() : value{ __m128i() } {} + // Zero constructor + simdjson_inline base() : value{__m128i()} {} - // Conversion from SIMD register - simdjson_inline base(const __m128i _value) : value(_value) {} + // 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; } + // 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(this); *this_cast = *this_cast | other; return *this_cast; } - simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } - simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } - }; + // 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(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; - template> - struct base8 : base> { - typedef uint16_t bitmask_t; - typedef uint32_t bitmask2_t; + template> + struct base8: base> { + typedef uint16_t bitmask_t; + typedef uint32_t bitmask2_t; - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m128i _value) : base>(_value) {} + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m128i _value) : base>(_value) {} - friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm_cmpeq_epi8(lhs, rhs); } + friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm_cmpeq_epi8(lhs, rhs); } - static const int SIZE = sizeof(base>::value); + static const int SIZE = sizeof(base>::value); - template - simdjson_inline simd8 prev(const simd8 prev_chunk) const { - return _mm_alignr_epi8(*this, prev_chunk, 16 - N); - } - }; + template + simdjson_inline simd8 prev(const simd8 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 : base8 { - static simdjson_inline simd8 splat(bool _value) { return _mm_set1_epi8(uint8_t(-(!!_value))); } + // SIMD byte mask type (returned by things like eq and gt) + template<> + struct simd8: base8 { + static simdjson_inline simd8 splat(bool _value) { return _mm_set1_epi8(uint8_t(-(!!_value))); } - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m128i _value) : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) : base8(splat(_value)) {} + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m128i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(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 operator~() const { return *this ^ true; } - }; + 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 operator~() const { return *this ^ true; } + }; - template - struct base8_numeric : base8 { - static simdjson_inline simd8 splat(T _value) { return _mm_set1_epi8(_value); } - static simdjson_inline simd8 zero() { return _mm_setzero_si128(); } - static simdjson_inline simd8 load(const T values[16]) { - return _mm_loadu_si128(reinterpret_cast(values)); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdjson_inline simd8 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + template + struct base8_numeric: base8 { + static simdjson_inline simd8 splat(T _value) { return _mm_set1_epi8(_value); } + static simdjson_inline simd8 zero() { return _mm_setzero_si128(); } + static simdjson_inline simd8 load(const T values[16]) { + return _mm_loadu_si128(reinterpret_cast(values)); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - simdjson_inline base8_numeric() : base8() {} - simdjson_inline base8_numeric(const __m128i _value) : base8(_value) {} + simdjson_inline base8_numeric() : base8() {} + simdjson_inline base8_numeric(const __m128i _value) : base8(_value) {} - // Store to array - simdjson_inline void store(T dst[16]) const { return _mm_storeu_si128(reinterpret_cast<__m128i*>(dst), *this); } + // 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 operator~() const { return *this ^ 0xFFu; } + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - // Addition/subtraction are the same for signed and unsigned - simdjson_inline simd8 operator+(const simd8 other) const { return _mm_add_epi8(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return _mm_sub_epi8(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { return _mm_add_epi8(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return _mm_sub_epi8(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return _mm_shuffle_epi8(lookup_table, *this); - } + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 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 compress(uint32_t mask) would be - // sensible, but the AVX ISA makes this kind of approach difficult. - template - 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(pshufb_combine_table + pop1 * 8)); - __m128i answer = _mm_shuffle_epi8(pruned, compactmask); - _mm_storeu_si128(reinterpret_cast<__m128i*>(output), answer); - } + // 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 compress(uint32_t mask) would be + // sensible, but the AVX ISA makes this kind of approach difficult. + template + 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(pshufb_combine_table + pop1 * 8)); + __m128i answer = _mm_shuffle_epi8(pruned, compactmask); + _mm_storeu_si128(reinterpret_cast<__m128i *>(output), answer); + } - template - simdjson_inline simd8 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::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - }; + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; - // Signed bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + // Signed bytes + template<> + struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - // Order-sensitive comparisons - simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epi8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epi8(*this, other); } - simdjson_inline simd8 operator>(const simd8 other) const { return _mm_cmpgt_epi8(*this, other); } - simdjson_inline simd8 operator<(const simd8 other) const { return _mm_cmpgt_epi8(other, *this); } - }; + // Order-sensitive comparisons + simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epi8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epi8(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return _mm_cmpgt_epi8(*this, other); } + simdjson_inline simd8 operator<(const simd8 other) const { return _mm_cmpgt_epi8(other, *this); } + }; - // Unsigned bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + // Unsigned bytes + template<> + struct simd8: base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - // Saturated math - simdjson_inline simd8 saturating_add(const simd8 other) const { return _mm_adds_epu8(*this, other); } - simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm_subs_epu8(*this, other); } + // Saturated math + simdjson_inline simd8 saturating_add(const simd8 other) const { return _mm_adds_epu8(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm_subs_epu8(*this, other); } - // Order-specific operations - simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epu8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epu8(*this, other); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } - simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } - simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } - simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - simdjson_inline simd8 operator<(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + // Order-specific operations + simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epu8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epu8(*this, other); } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } + simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } + simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } + simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + simdjson_inline simd8 operator<(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - // Bit-specific operations - simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } - simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } - simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } - simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return _mm_testz_si128(*this, bits); } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } - template - simdjson_inline simd8 shr() const { return simd8(_mm_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } - template - simdjson_inline simd8 shl() const { return simd8(_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 - simdjson_inline int get_bit() const { return _mm_movemask_epi8(_mm_slli_epi16(*this, 7 - N)); } - }; + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } + simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } + simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } + simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return _mm_testz_si128(*this, bits); } + simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } + template + simdjson_inline simd8 shr() const { return simd8(_mm_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } + template + simdjson_inline simd8 shl() const { return simd8(_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 + simdjson_inline int get_bit() const { return _mm_movemask_epi8(_mm_slli_epi16(*this, 7-N)); } + }; - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 4, "Westmere kernel should use four registers per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 4, "Westmere kernel should use four registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{ chunk0, chunk1, chunk2, chunk3 } {} - simdjson_inline simd8x64(const T ptr[64]) : chunks{ simd8::load(ptr), simd8::load(ptr + 16), simd8::load(ptr + 32), simd8::load(ptr + 48) } {} + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::load(ptr+16), simd8::load(ptr+32), simd8::load(ptr+48)} {} - simdjson_inline void store(T ptr[64]) const { - this->chunks[0].store(ptr + sizeof(simd8) * 0); - this->chunks[1].store(ptr + sizeof(simd8) * 1); - this->chunks[2].store(ptr + sizeof(simd8) * 2); - this->chunks[3].store(ptr + sizeof(simd8) * 3); - } + simdjson_inline void store(T ptr[64]) const { + this->chunks[0].store(ptr+sizeof(simd8)*0); + this->chunks[1].store(ptr+sizeof(simd8)*1); + this->chunks[2].store(ptr+sizeof(simd8)*2); + this->chunks[3].store(ptr+sizeof(simd8)*3); + } - simdjson_inline simd8 reduce_or() const { - return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]); - } + simdjson_inline simd8 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 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 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] == mask, - this->chunks[1] == mask, - this->chunks[2] == mask, - this->chunks[3] == mask - ).to_bitmask(); - } + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + 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& other) const { - return simd8x64( - 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 eq(const simd8x64 &other) const { + return simd8x64( + 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] <= mask, - this->chunks[1] <= mask, - this->chunks[2] <= mask, - this->chunks[3] <= mask - ).to_bitmask(); - } - }; // struct simd8x64 + simdjson_inline uint64_t lteq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] <= mask, + this->chunks[1] <= mask, + this->chunks[2] <= mask, + this->chunks[3] <= mask + ).to_bitmask(); + } + }; // struct simd8x64 - } // namespace simd - } // unnamed namespace - } // namespace westmere +} // 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 { +namespace westmere { +namespace { - using namespace simd; +using namespace simd; - // Holds backslashes and quotes locations. - struct backslash_and_quote { - public: - static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t* src, uint8_t* dst); +// 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); } + 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 + 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 v0(src); - simd8 v1(src + 16); - v0.store(dst); - v1.store(dst + 16); - uint64_t bs_and_quote = simd8x64(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); - return { - uint32_t(bs_and_quote), // bs_bits - uint32_t(bs_and_quote >> 32) // quote_bits - }; - } +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 v0(src); + simd8 v1(src + 16); + v0.store(dst); + v1.store(dst + 16); + uint64_t bs_and_quote = simd8x64(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); + return { + uint32_t(bs_and_quote), // bs_bits + uint32_t(bs_and_quote >> 32) // quote_bits + }; +} - } // unnamed namespace - } // namespace westmere + +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 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 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 @@ -34294,10 +35987,12 @@ namespace simdjson { /* 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_LSX */ -/* amalgamation skipped (editor-only): #include "simdjson/lsx/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 */ @@ -34307,22 +36002,22 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { +namespace westmere { - struct open_container; - class dom_parser_implementation; +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 - }; +/** + * 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 westmere } // namespace simdjson #endif // SIMDJSON_GENERIC_BASE_H @@ -34339,99 +36034,97 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { - namespace jsoncharutils { +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]; - } +// 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]; - } +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; - } +// 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 - } +// 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; - } +// 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 jsoncharutils +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_GENERIC_JSONCHARUTILS_H @@ -34449,69 +36142,69 @@ namespace simdjson { #include namespace simdjson { - namespace westmere { - namespace { - /// @private - namespace atomparsing { +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; } +// 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; } - // 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); - } +// 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); +} - 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) { + 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_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) { + 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_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) { + 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; } - } +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; } +} - } // namespace atomparsing - } // unnamed namespace - } // namespace westmere +} // namespace atomparsing +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_GENERIC_ATOMPARSING_H @@ -34527,83 +36220,83 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { +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 +// 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"); +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_containers{}; - /** Whether each open container is a [ or { */ - std::unique_ptr 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{}; +class dom_parser_implementation final : public internal::dom_parser_implementation { +public: + /** Tape location of each open { or [ */ + std::unique_ptr open_containers{}; + /** Whether each open container is a [ or { */ + std::unique_ptr 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; + 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); + 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 westmere } // namespace simdjson namespace simdjson { - namespace westmere { +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; +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) { return CAPACITY; } - // Stage 1 index output - size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; - 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; +// 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) { return CAPACITY; } + // Stage 1 index output + size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; + 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; - } + _capacity = capacity; + return SUCCESS; +} - inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { - // 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; } +inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { + // 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; - } + _max_depth = max_depth; + return SUCCESS; +} - } // namespace westmere +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H @@ -34618,129 +36311,148 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { +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 : public internal::implementation_simdjson_result_base { - * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} - * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} - * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} - * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} - * // Your extra methods here - * } - * - * Then any method returning simdjson_result will be chainable with your methods. - */ - template - struct implementation_simdjson_result_base { +// 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 : public internal::implementation_simdjson_result_base { + * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} + * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} + * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} + * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} + * // Your extra methods here + * } + * + * Then any method returning simdjson_result will be chainable with your methods. + */ +template +struct implementation_simdjson_result_base { - /** - * Create a new empty result with error = UNINITIALIZED. - */ - simdjson_inline implementation_simdjson_result_base() noexcept = default; + /** + * 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 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 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; + /** + * 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 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; + /** + * 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_inline error_code error() const 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& value() & noexcept(false); + /** + * 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); - /** - * Take the result value (move it). - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline T&& value() && 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&& take_value() && noexcept(false); + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& 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); + /** + * 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; - 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 + /** + * 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; - } // namespace westmere + 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 @@ -34761,8 +36473,8 @@ namespace simdjson { #include namespace simdjson { - namespace westmere { - namespace numberparsing { +namespace westmere { +namespace numberparsing { #ifdef JSON_TEST_NUMBERS #define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) @@ -34778,540 +36490,547 @@ namespace simdjson { #define BIGINT_NUMBER(SRC) (BIGINT_ERROR) #endif - namespace { +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(negative)) << 63); - std::memcpy(&d, &mantissa, sizeof(d)); - return d; - } +// 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(negative)) << 63); + std::memcpy(&d, &mantissa, sizeof(d)); + return d; +} - // 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 +// 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) + if (0 <= power && power <= 22 && i <= 9007199254740991) #else - if (-22 <= power && power <= 22 && i <= 9007199254740991) + 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 + { + // 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. + // 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; - } + // 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 - // approximatively 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; + // 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 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. - simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]); - // 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. + // 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 - // 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. - simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); - 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); + // 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. - // 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 - } - } + // 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); - mantissa += mantissa & 1; - mantissa >>= 1; + // 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 + } + } - // 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; - } + mantissa += mantissa & 1; + mantissa >>= 1; - // 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(ptr)); - // We do not accept infinite values. + // 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; +} - // 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::max)() || *outDouble < std::numeric_limits::lowest()); - } +// 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(ptr)); + // We do not accept infinite values. - static bool parse_float_fallback(const uint8_t* ptr, const uint8_t* end_ptr, double* outDouble) { - *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(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::max)() || *outDouble < std::numeric_limits::lowest()); +} - // 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::max)() || *outDouble < std::numeric_limits::lowest()); - } +static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) { + *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(end_ptr)); + // We do not accept infinite values. - // 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); - } + // 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::max)() || *outDouble < std::numeric_limits::lowest()); +} - template - 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(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; - } +// 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); +} - simdjson_inline bool is_digit(const uint8_t c) { - return static_cast(c - '0') <= 9; - } +template +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(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 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; +simdjson_inline bool is_digit(const uint8_t c) { + return static_cast(c - '0') <= 9; +} + +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; #ifdef SIMDJSON_SWAR_NUMBER_PARSING #if SIMDJSON_SWAR_NUMBER_PARSING - // this helps if we have lots of decimals! - // this turns out to be frequent enough. - if (is_made_of_eight_digits_fast(p)) { - i = i * 100000000 + parse_eight_digits_unrolled(p); - p += 8; - } + // this helps if we have lots of decimals! + // this turns out to be frequent enough. + if (is_made_of_eight_digits_fast(p)) { + i = i * 100000000 + parse_eight_digits_unrolled(p); + p += 8; + } #endif // SIMDJSON_SWAR_NUMBER_PARSING #endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING - // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) - if (parse_digit(*p, i)) { ++p; } - while (parse_digit(*p, i)) { p++; } - exponent = first_after_period - p; - // Decimal without digits (123.) is illegal - if (exponent == 0) { - return INVALID_NUMBER(src); - } - return SUCCESS; - } + // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) + if (parse_digit(*p, i)) { ++p; } + while (parse_digit(*p, i)) { p++; } + exponent = first_after_period - p; + // Decimal without digits (123.) is illegal + if (exponent == 0) { + return INVALID_NUMBER(src); + } + return SUCCESS; +} - 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 +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. + // 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. + // 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 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; - } + // 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 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); - } +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 +} // unnamed namespace - /** @private */ - static 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 */ +static 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 - 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; - } +/** @private */ +template +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 - simdjson_inline error_code parse_number(const uint8_t* const src, W& writer); +// 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 +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 +// for performance analysis, it is sometimes useful to skip parsing #ifdef SIMDJSON_SKIPNUMBERPARSING - template - simdjson_inline error_code parse_number(const uint8_t* const, W& writer) { - writer.append_s64(0); // always write zero - return SUCCESS; // always succeeds - } +template +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 parse_unsigned(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline bool is_negative(const uint8_t* src) noexcept { return false; } - simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t* src) noexcept { return false; } - simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t* src) noexcept { return number_type::signed_integer; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t * src) noexcept { return number_type::signed_integer; } #else // parse the number at src @@ -35323,744 +37042,753 @@ namespace simdjson { // content and append a space before calling this function. // // Our objective is accurate parsing (ULP of 0) at high speed. - template - 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)) { 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 INVALID_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 { - WRITE_INTEGER(negative ? (~i + 1) : i, src, writer); - } - 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 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; - 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); - // 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 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 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; - 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); - // 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 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; - 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); - // 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 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 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; - while (parse_digit(*src, i)) { src++; } - - // 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 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; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (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 == '.')) { - p++; - const uint8_t* start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (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 = 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; - } - - simdjson_unused simdjson_inline bool is_negative(const uint8_t* src) noexcept { - return (*src == '-'); - } - - simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t* src) noexcept { - bool negative = (*src == '-'); - src += uint8_t(negative); - const uint8_t* p = src; - while (static_cast(*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 get_number_type(const uint8_t* src) noexcept { - bool negative = (*src == '-'); - src += uint8_t(negative); - const uint8_t* p = src; - while (static_cast(*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("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; - } - 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("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 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 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; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (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 == '.')) { - p++; - const uint8_t* start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (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 = 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; - } - - } // unnamed namespace +template +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)) { 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 INVALID_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 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; + 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); + // 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 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 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; + 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); + // 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 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; + 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); + // 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 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 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; + while (parse_digit(*src, i)) { src++; } + + // 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { + return (*src == '-'); +} + +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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 get_number_type(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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("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("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 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +} // unnamed namespace #endif // SIMDJSON_SKIPNUMBERPARSING - } // namespace numberparsing +} // 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; - } +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 westmere } // namespace simdjson #endif // SIMDJSON_GENERIC_NUMBERPARSING_H @@ -36077,84 +37805,113 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { +namespace westmere { - // - // internal::implementation_simdjson_result_base inline implementation - // +// +// internal::implementation_simdjson_result_base inline implementation +// - template - simdjson_inline void implementation_simdjson_result_base::tie(T& value, error_code& error) && noexcept { - error = this->second; - if (!error) { - value = std::forward>(*this).first; - } - } +template +simdjson_inline void implementation_simdjson_result_base::tie(T &value, error_code &error) && noexcept { + error = this->second; + if (!error) { + value = std::forward>(*this).first; + } +} - template - simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T& value) && noexcept { - error_code error; - std::forward>(*this).tie(value, error); - return error; - } +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T &value) && noexcept { + error_code error; + std::forward>(*this).tie(value, error); + return error; +} - template - simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { - return this->second; - } +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { + return this->second; +} + + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} #if SIMDJSON_EXCEPTIONS - template - simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return this->first; - } +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} - template - simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { - return std::forward>(*this).take_value(); - } +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} - template - simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return std::forward(this->first); - } +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} - template - simdjson_inline implementation_simdjson_result_base::operator T && () && noexcept(false) { - return std::forward>(*this).take_value(); - } + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + +template +simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return this->first; +} + +template +simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { + return std::forward>(*this).take_value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return std::forward(this->first); +} + +template +simdjson_inline implementation_simdjson_result_base::operator T&&() && noexcept(false) { + return std::forward>(*this).take_value(); +} #endif // SIMDJSON_EXCEPTIONS - template - simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { - return this->first; - } +template +simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { + return this->first; +} - template - simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { - return this->first; - } +template +simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { + return this->first; +} - template - simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { - return std::forward(this->first); - } +template +simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { + return std::forward(this->first); +} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T&& value, error_code error) noexcept - : first{ std::forward(value) }, second{ error } {} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept - : implementation_simdjson_result_base(T{}, error) {} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T&& value) noexcept - : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value, error_code error) noexcept + : first{std::forward(value)}, second{error} {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept + : implementation_simdjson_result_base(T{}, error) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value) noexcept + : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} - } // namespace westmere +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H @@ -36189,24 +37946,24 @@ SIMDJSON_UNTARGET_REGION // The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_WESTMERE namespace simdjson { - namespace westmere { +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& 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; - }; +/** + * @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& 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 westmere } // namespace simdjson #endif // SIMDJSON_WESTMERE_IMPLEMENTATION_H @@ -36227,23 +37984,23 @@ namespace simdjson { // 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 { +/** + * Implementation for Westmere (Intel SSE4.2). + */ +namespace westmere { - class implementation; +class implementation; - namespace { - namespace simd { +namespace { +namespace simd { - template struct simd8; - template struct simd8x64; +template struct simd8; +template struct simd8x64; - } // namespace simd - } // unnamed namespace +} // namespace simd +} // unnamed namespace - } // namespace westmere +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_WESTMERE_BASE_H @@ -36298,73 +38055,73 @@ SIMDJSON_TARGET_REGION("sse4.2,pclmul,popcnt") /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { +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) { +// 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; + 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); + 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 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) { +/* 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; + 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); + 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 - } +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); - } +simdjson_inline long long int count_ones(uint64_t input_num) { + return _popcnt64(input_num); +} #endif - simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, - uint64_t* result) { +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { #if SIMDJSON_REGULAR_VISUAL_STUDIO - return _addcarry_u64(0, value1, value2, - reinterpret_cast(result)); + return _addcarry_u64(0, value1, value2, + reinterpret_cast(result)); #else - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); #endif - } +} - } // unnamed namespace - } // namespace westmere +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_WESTMERE_BITMANIPULATION_H @@ -36380,24 +38137,24 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { +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); - } +// +// 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 +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_WESTMERE_BITMASK_H @@ -36418,23 +38175,23 @@ namespace simdjson { // 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 { +/** + * Implementation for Westmere (Intel SSE4.2). + */ +namespace westmere { - class implementation; +class implementation; - namespace { - namespace simd { +namespace { +namespace simd { - template struct simd8; - template struct simd8x64; +template struct simd8; +template struct simd8x64; - } // namespace simd - } // unnamed namespace +} // namespace simd +} // unnamed namespace - } // namespace westmere +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_WESTMERE_BASE_H @@ -36479,49 +38236,49 @@ static_assert(sizeof(__m128i) <= simdjson::SIMDJSON_PADDING, "insufficient paddi /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace numberparsing { +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(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 */ +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(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; +/** @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; + // 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 + 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); + __uint128_t r = (static_cast<__uint128_t>(value1)) * value2; + answer.low = uint64_t(r); + answer.high = uint64_t(r >> 64); #endif - return answer; - } + return answer; +} - } // namespace numberparsing - } // namespace westmere +} // namespace numberparsing +} // namespace westmere } // namespace simdjson #define SIMDJSON_SWAR_NUMBER_PARSING 1 @@ -36540,331 +38297,332 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { - namespace simd { +namespace westmere { +namespace { +namespace simd { - template - struct base { - __m128i value; + template + struct base { + __m128i value; - // Zero constructor - simdjson_inline base() : value{ __m128i() } {} + // Zero constructor + simdjson_inline base() : value{__m128i()} {} - // Conversion from SIMD register - simdjson_inline base(const __m128i _value) : value(_value) {} + // 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; } + // 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(this); *this_cast = *this_cast | other; return *this_cast; } - simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } - simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } - }; + // 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(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; - template> - struct base8 : base> { - typedef uint16_t bitmask_t; - typedef uint32_t bitmask2_t; + template> + struct base8: base> { + typedef uint16_t bitmask_t; + typedef uint32_t bitmask2_t; - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m128i _value) : base>(_value) {} + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m128i _value) : base>(_value) {} - friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm_cmpeq_epi8(lhs, rhs); } + friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm_cmpeq_epi8(lhs, rhs); } - static const int SIZE = sizeof(base>::value); + static const int SIZE = sizeof(base>::value); - template - simdjson_inline simd8 prev(const simd8 prev_chunk) const { - return _mm_alignr_epi8(*this, prev_chunk, 16 - N); - } - }; + template + simdjson_inline simd8 prev(const simd8 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 : base8 { - static simdjson_inline simd8 splat(bool _value) { return _mm_set1_epi8(uint8_t(-(!!_value))); } + // SIMD byte mask type (returned by things like eq and gt) + template<> + struct simd8: base8 { + static simdjson_inline simd8 splat(bool _value) { return _mm_set1_epi8(uint8_t(-(!!_value))); } - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m128i _value) : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) : base8(splat(_value)) {} + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m128i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(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 operator~() const { return *this ^ true; } - }; + 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 operator~() const { return *this ^ true; } + }; - template - struct base8_numeric : base8 { - static simdjson_inline simd8 splat(T _value) { return _mm_set1_epi8(_value); } - static simdjson_inline simd8 zero() { return _mm_setzero_si128(); } - static simdjson_inline simd8 load(const T values[16]) { - return _mm_loadu_si128(reinterpret_cast(values)); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdjson_inline simd8 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + template + struct base8_numeric: base8 { + static simdjson_inline simd8 splat(T _value) { return _mm_set1_epi8(_value); } + static simdjson_inline simd8 zero() { return _mm_setzero_si128(); } + static simdjson_inline simd8 load(const T values[16]) { + return _mm_loadu_si128(reinterpret_cast(values)); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - simdjson_inline base8_numeric() : base8() {} - simdjson_inline base8_numeric(const __m128i _value) : base8(_value) {} + simdjson_inline base8_numeric() : base8() {} + simdjson_inline base8_numeric(const __m128i _value) : base8(_value) {} - // Store to array - simdjson_inline void store(T dst[16]) const { return _mm_storeu_si128(reinterpret_cast<__m128i*>(dst), *this); } + // 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 operator~() const { return *this ^ 0xFFu; } + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - // Addition/subtraction are the same for signed and unsigned - simdjson_inline simd8 operator+(const simd8 other) const { return _mm_add_epi8(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return _mm_sub_epi8(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { return _mm_add_epi8(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return _mm_sub_epi8(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return _mm_shuffle_epi8(lookup_table, *this); - } + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 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 compress(uint32_t mask) would be - // sensible, but the AVX ISA makes this kind of approach difficult. - template - 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(pshufb_combine_table + pop1 * 8)); - __m128i answer = _mm_shuffle_epi8(pruned, compactmask); - _mm_storeu_si128(reinterpret_cast<__m128i*>(output), answer); - } + // 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 compress(uint32_t mask) would be + // sensible, but the AVX ISA makes this kind of approach difficult. + template + 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(pshufb_combine_table + pop1 * 8)); + __m128i answer = _mm_shuffle_epi8(pruned, compactmask); + _mm_storeu_si128(reinterpret_cast<__m128i *>(output), answer); + } - template - simdjson_inline simd8 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::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - }; + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; - // Signed bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + // Signed bytes + template<> + struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - // Order-sensitive comparisons - simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epi8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epi8(*this, other); } - simdjson_inline simd8 operator>(const simd8 other) const { return _mm_cmpgt_epi8(*this, other); } - simdjson_inline simd8 operator<(const simd8 other) const { return _mm_cmpgt_epi8(other, *this); } - }; + // Order-sensitive comparisons + simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epi8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epi8(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return _mm_cmpgt_epi8(*this, other); } + simdjson_inline simd8 operator<(const simd8 other) const { return _mm_cmpgt_epi8(other, *this); } + }; - // Unsigned bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + // Unsigned bytes + template<> + struct simd8: base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - // Saturated math - simdjson_inline simd8 saturating_add(const simd8 other) const { return _mm_adds_epu8(*this, other); } - simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm_subs_epu8(*this, other); } + // Saturated math + simdjson_inline simd8 saturating_add(const simd8 other) const { return _mm_adds_epu8(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm_subs_epu8(*this, other); } - // Order-specific operations - simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epu8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epu8(*this, other); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } - simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } - simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } - simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - simdjson_inline simd8 operator<(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + // Order-specific operations + simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epu8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epu8(*this, other); } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } + simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } + simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } + simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + simdjson_inline simd8 operator<(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - // Bit-specific operations - simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } - simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } - simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } - simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return _mm_testz_si128(*this, bits); } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } - template - simdjson_inline simd8 shr() const { return simd8(_mm_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } - template - simdjson_inline simd8 shl() const { return simd8(_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 - simdjson_inline int get_bit() const { return _mm_movemask_epi8(_mm_slli_epi16(*this, 7 - N)); } - }; + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } + simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } + simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } + simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return _mm_testz_si128(*this, bits); } + simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } + template + simdjson_inline simd8 shr() const { return simd8(_mm_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } + template + simdjson_inline simd8 shl() const { return simd8(_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 + simdjson_inline int get_bit() const { return _mm_movemask_epi8(_mm_slli_epi16(*this, 7-N)); } + }; - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 4, "Westmere kernel should use four registers per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 4, "Westmere kernel should use four registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{ chunk0, chunk1, chunk2, chunk3 } {} - simdjson_inline simd8x64(const T ptr[64]) : chunks{ simd8::load(ptr), simd8::load(ptr + 16), simd8::load(ptr + 32), simd8::load(ptr + 48) } {} + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::load(ptr+16), simd8::load(ptr+32), simd8::load(ptr+48)} {} - simdjson_inline void store(T ptr[64]) const { - this->chunks[0].store(ptr + sizeof(simd8) * 0); - this->chunks[1].store(ptr + sizeof(simd8) * 1); - this->chunks[2].store(ptr + sizeof(simd8) * 2); - this->chunks[3].store(ptr + sizeof(simd8) * 3); - } + simdjson_inline void store(T ptr[64]) const { + this->chunks[0].store(ptr+sizeof(simd8)*0); + this->chunks[1].store(ptr+sizeof(simd8)*1); + this->chunks[2].store(ptr+sizeof(simd8)*2); + this->chunks[3].store(ptr+sizeof(simd8)*3); + } - simdjson_inline simd8 reduce_or() const { - return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]); - } + simdjson_inline simd8 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 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 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] == mask, - this->chunks[1] == mask, - this->chunks[2] == mask, - this->chunks[3] == mask - ).to_bitmask(); - } + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + 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& other) const { - return simd8x64( - 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 eq(const simd8x64 &other) const { + return simd8x64( + 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] <= mask, - this->chunks[1] <= mask, - this->chunks[2] <= mask, - this->chunks[3] <= mask - ).to_bitmask(); - } - }; // struct simd8x64 + simdjson_inline uint64_t lteq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] <= mask, + this->chunks[1] <= mask, + this->chunks[2] <= mask, + this->chunks[3] <= mask + ).to_bitmask(); + } + }; // struct simd8x64 - } // namespace simd - } // unnamed namespace - } // namespace westmere +} // namespace simd +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_WESTMERE_SIMD_INPUT_H @@ -36885,73 +38643,73 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { +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) { +// 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; + 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); + 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 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) { +/* 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; + 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); + 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 - } +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); - } +simdjson_inline long long int count_ones(uint64_t input_num) { + return _popcnt64(input_num); +} #endif - simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, - uint64_t* result) { +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { #if SIMDJSON_REGULAR_VISUAL_STUDIO - return _addcarry_u64(0, value1, value2, - reinterpret_cast(result)); + return _addcarry_u64(0, value1, value2, + reinterpret_cast(result)); #else - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); #endif - } +} - } // unnamed namespace - } // namespace westmere +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_WESTMERE_BITMANIPULATION_H @@ -36968,374 +38726,400 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { - namespace simd { +namespace westmere { +namespace { +namespace simd { - template - struct base { - __m128i value; + template + struct base { + __m128i value; - // Zero constructor - simdjson_inline base() : value{ __m128i() } {} + // Zero constructor + simdjson_inline base() : value{__m128i()} {} - // Conversion from SIMD register - simdjson_inline base(const __m128i _value) : value(_value) {} + // 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; } + // 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(this); *this_cast = *this_cast | other; return *this_cast; } - simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } - simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } - }; + // 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(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; - template> - struct base8 : base> { - typedef uint16_t bitmask_t; - typedef uint32_t bitmask2_t; + template> + struct base8: base> { + typedef uint16_t bitmask_t; + typedef uint32_t bitmask2_t; - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m128i _value) : base>(_value) {} + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m128i _value) : base>(_value) {} - friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm_cmpeq_epi8(lhs, rhs); } + friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm_cmpeq_epi8(lhs, rhs); } - static const int SIZE = sizeof(base>::value); + static const int SIZE = sizeof(base>::value); - template - simdjson_inline simd8 prev(const simd8 prev_chunk) const { - return _mm_alignr_epi8(*this, prev_chunk, 16 - N); - } - }; + template + simdjson_inline simd8 prev(const simd8 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 : base8 { - static simdjson_inline simd8 splat(bool _value) { return _mm_set1_epi8(uint8_t(-(!!_value))); } + // SIMD byte mask type (returned by things like eq and gt) + template<> + struct simd8: base8 { + static simdjson_inline simd8 splat(bool _value) { return _mm_set1_epi8(uint8_t(-(!!_value))); } - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m128i _value) : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) : base8(splat(_value)) {} + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m128i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(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 operator~() const { return *this ^ true; } - }; + 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 operator~() const { return *this ^ true; } + }; - template - struct base8_numeric : base8 { - static simdjson_inline simd8 splat(T _value) { return _mm_set1_epi8(_value); } - static simdjson_inline simd8 zero() { return _mm_setzero_si128(); } - static simdjson_inline simd8 load(const T values[16]) { - return _mm_loadu_si128(reinterpret_cast(values)); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdjson_inline simd8 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + template + struct base8_numeric: base8 { + static simdjson_inline simd8 splat(T _value) { return _mm_set1_epi8(_value); } + static simdjson_inline simd8 zero() { return _mm_setzero_si128(); } + static simdjson_inline simd8 load(const T values[16]) { + return _mm_loadu_si128(reinterpret_cast(values)); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - simdjson_inline base8_numeric() : base8() {} - simdjson_inline base8_numeric(const __m128i _value) : base8(_value) {} + simdjson_inline base8_numeric() : base8() {} + simdjson_inline base8_numeric(const __m128i _value) : base8(_value) {} - // Store to array - simdjson_inline void store(T dst[16]) const { return _mm_storeu_si128(reinterpret_cast<__m128i*>(dst), *this); } + // 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 operator~() const { return *this ^ 0xFFu; } + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - // Addition/subtraction are the same for signed and unsigned - simdjson_inline simd8 operator+(const simd8 other) const { return _mm_add_epi8(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return _mm_sub_epi8(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { return _mm_add_epi8(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return _mm_sub_epi8(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return _mm_shuffle_epi8(lookup_table, *this); - } + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 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 compress(uint32_t mask) would be - // sensible, but the AVX ISA makes this kind of approach difficult. - template - 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(pshufb_combine_table + pop1 * 8)); - __m128i answer = _mm_shuffle_epi8(pruned, compactmask); - _mm_storeu_si128(reinterpret_cast<__m128i*>(output), answer); - } + // 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 compress(uint32_t mask) would be + // sensible, but the AVX ISA makes this kind of approach difficult. + template + 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(pshufb_combine_table + pop1 * 8)); + __m128i answer = _mm_shuffle_epi8(pruned, compactmask); + _mm_storeu_si128(reinterpret_cast<__m128i *>(output), answer); + } - template - simdjson_inline simd8 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::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - }; + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; - // Signed bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + // Signed bytes + template<> + struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - // Order-sensitive comparisons - simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epi8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epi8(*this, other); } - simdjson_inline simd8 operator>(const simd8 other) const { return _mm_cmpgt_epi8(*this, other); } - simdjson_inline simd8 operator<(const simd8 other) const { return _mm_cmpgt_epi8(other, *this); } - }; + // Order-sensitive comparisons + simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epi8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epi8(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return _mm_cmpgt_epi8(*this, other); } + simdjson_inline simd8 operator<(const simd8 other) const { return _mm_cmpgt_epi8(other, *this); } + }; - // Unsigned bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + // Unsigned bytes + template<> + struct simd8: base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - // Saturated math - simdjson_inline simd8 saturating_add(const simd8 other) const { return _mm_adds_epu8(*this, other); } - simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm_subs_epu8(*this, other); } + // Saturated math + simdjson_inline simd8 saturating_add(const simd8 other) const { return _mm_adds_epu8(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm_subs_epu8(*this, other); } - // Order-specific operations - simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epu8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epu8(*this, other); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } - simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } - simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } - simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - simdjson_inline simd8 operator<(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + // Order-specific operations + simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epu8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epu8(*this, other); } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } + simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } + simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } + simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + simdjson_inline simd8 operator<(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - // Bit-specific operations - simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } - simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } - simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } - simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return _mm_testz_si128(*this, bits); } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } - template - simdjson_inline simd8 shr() const { return simd8(_mm_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } - template - simdjson_inline simd8 shl() const { return simd8(_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 - simdjson_inline int get_bit() const { return _mm_movemask_epi8(_mm_slli_epi16(*this, 7 - N)); } - }; + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } + simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } + simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } + simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return _mm_testz_si128(*this, bits); } + simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } + template + simdjson_inline simd8 shr() const { return simd8(_mm_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } + template + simdjson_inline simd8 shl() const { return simd8(_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 + simdjson_inline int get_bit() const { return _mm_movemask_epi8(_mm_slli_epi16(*this, 7-N)); } + }; - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 4, "Westmere kernel should use four registers per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 4, "Westmere kernel should use four registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{ chunk0, chunk1, chunk2, chunk3 } {} - simdjson_inline simd8x64(const T ptr[64]) : chunks{ simd8::load(ptr), simd8::load(ptr + 16), simd8::load(ptr + 32), simd8::load(ptr + 48) } {} + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::load(ptr+16), simd8::load(ptr+32), simd8::load(ptr+48)} {} - simdjson_inline void store(T ptr[64]) const { - this->chunks[0].store(ptr + sizeof(simd8) * 0); - this->chunks[1].store(ptr + sizeof(simd8) * 1); - this->chunks[2].store(ptr + sizeof(simd8) * 2); - this->chunks[3].store(ptr + sizeof(simd8) * 3); - } + simdjson_inline void store(T ptr[64]) const { + this->chunks[0].store(ptr+sizeof(simd8)*0); + this->chunks[1].store(ptr+sizeof(simd8)*1); + this->chunks[2].store(ptr+sizeof(simd8)*2); + this->chunks[3].store(ptr+sizeof(simd8)*3); + } - simdjson_inline simd8 reduce_or() const { - return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]); - } + simdjson_inline simd8 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 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 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] == mask, - this->chunks[1] == mask, - this->chunks[2] == mask, - this->chunks[3] == mask - ).to_bitmask(); - } + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + 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& other) const { - return simd8x64( - 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 eq(const simd8x64 &other) const { + return simd8x64( + 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] <= mask, - this->chunks[1] <= mask, - this->chunks[2] <= mask, - this->chunks[3] <= mask - ).to_bitmask(); - } - }; // struct simd8x64 + simdjson_inline uint64_t lteq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] <= mask, + this->chunks[1] <= mask, + this->chunks[2] <= mask, + this->chunks[3] <= mask + ).to_bitmask(); + } + }; // struct simd8x64 - } // namespace simd - } // unnamed namespace - } // namespace westmere +} // 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 { +namespace westmere { +namespace { - using namespace simd; +using namespace simd; - // Holds backslashes and quotes locations. - struct backslash_and_quote { - public: - static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t* src, uint8_t* dst); +// 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); } + 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 + 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 v0(src); - simd8 v1(src + 16); - v0.store(dst); - v1.store(dst + 16); - uint64_t bs_and_quote = simd8x64(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); - return { - uint32_t(bs_and_quote), // bs_bits - uint32_t(bs_and_quote >> 32) // quote_bits - }; - } +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 v0(src); + simd8 v1(src + 16); + v0.store(dst); + v1.store(dst + 16); + uint64_t bs_and_quote = simd8x64(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); + return { + uint32_t(bs_and_quote), // bs_bits + uint32_t(bs_and_quote >> 32) // quote_bits + }; +} - } // unnamed namespace - } // namespace westmere + +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 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 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 @@ -37358,13 +39142,13 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { +namespace westmere { +namespace { - struct json_character_block; +struct json_character_block; - } // unnamed namespace - } // namespace westmere +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_BASE_H @@ -37381,14 +39165,14 @@ namespace simdjson { // Interface a dom parser implementation must fulfill namespace simdjson { - namespace westmere { - namespace { +namespace westmere { +namespace { - simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3); - simdjson_inline bool is_ascii(const simd8x64& input); +simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3); +simdjson_inline bool is_ascii(const simd8x64& input); - } // unnamed namespace - } // namespace westmere +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_DOM_PARSER_IMPLEMENTATION_H @@ -37403,22 +39187,22 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { +namespace westmere { +namespace { - struct json_character_block { - static simdjson_inline json_character_block classify(const simd::simd8x64& in); +struct json_character_block { + static simdjson_inline json_character_block classify(const simd::simd8x64& in); - simdjson_inline uint64_t whitespace() const noexcept { return _whitespace; } - simdjson_inline uint64_t op() const noexcept { return _op; } - simdjson_inline uint64_t scalar() const noexcept { return ~(op() | whitespace()); } + simdjson_inline uint64_t whitespace() const noexcept { return _whitespace; } + simdjson_inline uint64_t op() const noexcept { return _op; } + simdjson_inline uint64_t scalar() const noexcept { return ~(op() | whitespace()); } - uint64_t _whitespace; - uint64_t _op; - }; + uint64_t _whitespace; + uint64_t _op; +}; - } // unnamed namespace - } // namespace westmere +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_JSON_CHARACTER_BLOCK_H @@ -37437,30 +39221,30 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { - namespace stage1 { +namespace westmere { +namespace { +namespace stage1 { - class bit_indexer; - template - struct buf_block_reader; - struct json_block; - class json_minifier; - class json_scanner; - struct json_string_block; - class json_string_scanner; - class json_structural_indexer; +class bit_indexer; +template +struct buf_block_reader; +struct json_block; +class json_minifier; +class json_scanner; +struct json_string_block; +class json_string_scanner; +class json_structural_indexer; - } // namespace stage1 +} // namespace stage1 - namespace utf8_validation { - struct utf8_checker; - } // namespace utf8_validation +namespace utf8_validation { +struct utf8_checker; +} // namespace utf8_validation - using utf8_validation::utf8_checker; +using utf8_validation::utf8_checker; - } // unnamed namespace - } // namespace westmere +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_BASE_H @@ -37477,109 +39261,109 @@ namespace simdjson { #include namespace simdjson { - namespace westmere { - namespace { - namespace stage1 { +namespace westmere { +namespace { +namespace stage1 { - // Walks through a buffer in block-sized increments, loading the last part with spaces - template - struct buf_block_reader { - public: - simdjson_inline buf_block_reader(const uint8_t* _buf, size_t _len); - simdjson_inline size_t block_index(); - simdjson_inline bool has_full_block() const; - simdjson_inline const uint8_t* full_block() const; - /** - * Get the last block, padded with spaces. - * - * There will always be a last block, with at least 1 byte, unless len == 0 (in which case this - * function fills the buffer with spaces and returns 0. In particular, if len == STEP_SIZE there - * will be 0 full_blocks and 1 remainder block with STEP_SIZE bytes and no spaces for padding. - * - * @return the number of effective characters in the last block. - */ - simdjson_inline size_t get_remainder(uint8_t* dst) const; - simdjson_inline void advance(); - private: - const uint8_t* buf; - const size_t len; - const size_t lenminusstep; - size_t idx; - }; +// Walks through a buffer in block-sized increments, loading the last part with spaces +template +struct buf_block_reader { +public: + simdjson_inline buf_block_reader(const uint8_t *_buf, size_t _len); + simdjson_inline size_t block_index(); + simdjson_inline bool has_full_block() const; + simdjson_inline const uint8_t *full_block() const; + /** + * Get the last block, padded with spaces. + * + * There will always be a last block, with at least 1 byte, unless len == 0 (in which case this + * function fills the buffer with spaces and returns 0. In particular, if len == STEP_SIZE there + * will be 0 full_blocks and 1 remainder block with STEP_SIZE bytes and no spaces for padding. + * + * @return the number of effective characters in the last block. + */ + simdjson_inline size_t get_remainder(uint8_t *dst) const; + simdjson_inline void advance(); +private: + const uint8_t *buf; + const size_t len; + const size_t lenminusstep; + size_t idx; +}; - // Routines to print masks and text for debugging bitmask operations - simdjson_unused static char* format_input_text_64(const uint8_t* text) { - static char buf[sizeof(simd8x64) + 1]; - for (size_t i = 0; i < sizeof(simd8x64); i++) { - buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]); - } - buf[sizeof(simd8x64)] = '\0'; - return buf; - } +// Routines to print masks and text for debugging bitmask operations +simdjson_unused static char * format_input_text_64(const uint8_t *text) { + static char buf[sizeof(simd8x64) + 1]; + for (size_t i=0; i); i++) { + buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]); + } + buf[sizeof(simd8x64)] = '\0'; + return buf; +} - // Routines to print masks and text for debugging bitmask operations - simdjson_unused static char* format_input_text(const simd8x64& in) { - static char buf[sizeof(simd8x64) + 1]; - in.store(reinterpret_cast(buf)); - for (size_t i = 0; i < sizeof(simd8x64); i++) { - if (buf[i] < ' ') { buf[i] = '_'; } - } - buf[sizeof(simd8x64)] = '\0'; - return buf; - } +// Routines to print masks and text for debugging bitmask operations +simdjson_unused static char * format_input_text(const simd8x64& in) { + static char buf[sizeof(simd8x64) + 1]; + in.store(reinterpret_cast(buf)); + for (size_t i=0; i); i++) { + if (buf[i] < ' ') { buf[i] = '_'; } + } + buf[sizeof(simd8x64)] = '\0'; + return buf; +} - simdjson_unused static char* format_input_text(const simd8x64& in, uint64_t mask) { - static char buf[sizeof(simd8x64) + 1]; - in.store(reinterpret_cast(buf)); - for (size_t i = 0; i < sizeof(simd8x64); i++) { - if (buf[i] <= ' ') { buf[i] = '_'; } - if (!(mask & (size_t(1) << i))) { buf[i] = ' '; } - } - buf[sizeof(simd8x64)] = '\0'; - return buf; - } +simdjson_unused static char * format_input_text(const simd8x64& in, uint64_t mask) { + static char buf[sizeof(simd8x64) + 1]; + in.store(reinterpret_cast(buf)); + for (size_t i=0; i); i++) { + if (buf[i] <= ' ') { buf[i] = '_'; } + if (!(mask & (size_t(1) << i))) { buf[i] = ' '; } + } + buf[sizeof(simd8x64)] = '\0'; + return buf; +} - simdjson_unused static char* format_mask(uint64_t mask) { - static char buf[sizeof(simd8x64) + 1]; - for (size_t i = 0; i < 64; i++) { - buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' '; - } - buf[64] = '\0'; - return buf; - } +simdjson_unused static char * format_mask(uint64_t mask) { + static char buf[sizeof(simd8x64) + 1]; + for (size_t i=0; i<64; i++) { + buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' '; + } + buf[64] = '\0'; + return buf; +} - template - simdjson_inline buf_block_reader::buf_block_reader(const uint8_t* _buf, size_t _len) : buf{ _buf }, len{ _len }, lenminusstep{ len < STEP_SIZE ? 0 : len - STEP_SIZE }, idx{ 0 } {} +template +simdjson_inline buf_block_reader::buf_block_reader(const uint8_t *_buf, size_t _len) : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE}, idx{0} {} - template - simdjson_inline size_t buf_block_reader::block_index() { return idx; } +template +simdjson_inline size_t buf_block_reader::block_index() { return idx; } - template - simdjson_inline bool buf_block_reader::has_full_block() const { - return idx < lenminusstep; - } +template +simdjson_inline bool buf_block_reader::has_full_block() const { + return idx < lenminusstep; +} - template - simdjson_inline const uint8_t* buf_block_reader::full_block() const { - return &buf[idx]; - } +template +simdjson_inline const uint8_t *buf_block_reader::full_block() const { + return &buf[idx]; +} - template - simdjson_inline size_t buf_block_reader::get_remainder(uint8_t* dst) const { - if (len == idx) { return 0; } // memcpy(dst, null, 0) will trigger an error with some sanitizers - std::memset(dst, 0x20, STEP_SIZE); // std::memset STEP_SIZE because it's more efficient to write out 8 or 16 bytes at once. - std::memcpy(dst, buf + idx, len - idx); - return len - idx; - } +template +simdjson_inline size_t buf_block_reader::get_remainder(uint8_t *dst) const { + if(len == idx) { return 0; } // memcpy(dst, null, 0) will trigger an error with some sanitizers + std::memset(dst, 0x20, STEP_SIZE); // std::memset STEP_SIZE because it's more efficient to write out 8 or 16 bytes at once. + std::memcpy(dst, buf + idx, len - idx); + return len - idx; +} - template - simdjson_inline void buf_block_reader::advance() { - idx += STEP_SIZE; - } +template +simdjson_inline void buf_block_reader::advance() { + idx += STEP_SIZE; +} - } // namespace stage1 - } // unnamed namespace - } // namespace westmere +} // namespace stage1 +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_BUF_BLOCK_READER_H @@ -37595,145 +39379,145 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { - namespace stage1 { +namespace westmere { +namespace { +namespace stage1 { - /** - * Scans for escape characters in JSON, taking care with multiple backslashes (\\n vs. \n). - */ - struct json_escape_scanner { - /** The actual escape characters (the backslashes themselves). */ - uint64_t next_is_escaped = 0ULL; +/** + * Scans for escape characters in JSON, taking care with multiple backslashes (\\n vs. \n). + */ +struct json_escape_scanner { + /** The actual escape characters (the backslashes themselves). */ + uint64_t next_is_escaped = 0ULL; - struct escaped_and_escape { - /** - * Mask of escaped characters. - * - * ``` - * \n \\n \\\n \\\\n \ - * 0100100010100101000 - * n \ \ n \ \ - * ``` - */ - uint64_t escaped; - /** - * Mask of escape characters. - * - * ``` - * \n \\n \\\n \\\\n \ - * 1001000101001010001 - * \ \ \ \ \ \ \ - * ``` - */ - uint64_t escape; - }; + struct escaped_and_escape { + /** + * Mask of escaped characters. + * + * ``` + * \n \\n \\\n \\\\n \ + * 0100100010100101000 + * n \ \ n \ \ + * ``` + */ + uint64_t escaped; + /** + * Mask of escape characters. + * + * ``` + * \n \\n \\\n \\\\n \ + * 1001000101001010001 + * \ \ \ \ \ \ \ + * ``` + */ + uint64_t escape; + }; - /** - * Get a mask of both escape and escaped characters (the characters following a backslash). - * - * @param potential_escape A mask of the character that can escape others (but could be - * escaped itself). e.g. block.eq('\\') - */ - simdjson_really_inline escaped_and_escape next(uint64_t backslash) noexcept { + /** + * Get a mask of both escape and escaped characters (the characters following a backslash). + * + * @param potential_escape A mask of the character that can escape others (but could be + * escaped itself). e.g. block.eq('\\') + */ + simdjson_really_inline escaped_and_escape next(uint64_t backslash) noexcept { #if !SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT - if (!backslash) { return { next_escaped_without_backslashes(), 0 }; } + if (!backslash) { return {next_escaped_without_backslashes(), 0}; } #endif - // | | Mask (shows characters instead of 1's) | Depth | Instructions | - // |--------------------------------|----------------------------------------|-------|---------------------| - // | string | `\\n_\\\n___\\\n___\\\\___\\\\__\\\` | | | - // | | ` even odd even odd odd` | | | - // | potential_escape | ` \ \\\ \\\ \\\\ \\\\ \\\` | 1 | 1 (backslash & ~first_is_escaped) - // | escape_and_terminal_code | ` \n \ \n \ \n \ \ \ \ \ \` | 5 | 5 (next_escape_and_terminal_code()) - // | escaped | `\ \ n \ n \ \ \ \ \ ` X | 6 | 7 (escape_and_terminal_code ^ (potential_escape | first_is_escaped)) - // | escape | ` \ \ \ \ \ \ \ \ \ \` | 6 | 8 (escape_and_terminal_code & backslash) - // | first_is_escaped | `\ ` | 7 (*) | 9 (escape >> 63) () - // (*) this is not needed until the next iteration - uint64_t escape_and_terminal_code = next_escape_and_terminal_code(backslash & ~this->next_is_escaped); - uint64_t escaped = escape_and_terminal_code ^ (backslash | this->next_is_escaped); - uint64_t escape = escape_and_terminal_code & backslash; - this->next_is_escaped = escape >> 63; - return { escaped, escape }; - } + // | | Mask (shows characters instead of 1's) | Depth | Instructions | + // |--------------------------------|----------------------------------------|-------|---------------------| + // | string | `\\n_\\\n___\\\n___\\\\___\\\\__\\\` | | | + // | | ` even odd even odd odd` | | | + // | potential_escape | ` \ \\\ \\\ \\\\ \\\\ \\\` | 1 | 1 (backslash & ~first_is_escaped) + // | escape_and_terminal_code | ` \n \ \n \ \n \ \ \ \ \ \` | 5 | 5 (next_escape_and_terminal_code()) + // | escaped | `\ \ n \ n \ \ \ \ \ ` X | 6 | 7 (escape_and_terminal_code ^ (potential_escape | first_is_escaped)) + // | escape | ` \ \ \ \ \ \ \ \ \ \` | 6 | 8 (escape_and_terminal_code & backslash) + // | first_is_escaped | `\ ` | 7 (*) | 9 (escape >> 63) () + // (*) this is not needed until the next iteration + uint64_t escape_and_terminal_code = next_escape_and_terminal_code(backslash & ~this->next_is_escaped); + uint64_t escaped = escape_and_terminal_code ^ (backslash | this->next_is_escaped); + uint64_t escape = escape_and_terminal_code & backslash; + this->next_is_escaped = escape >> 63; + return {escaped, escape}; + } - private: - static constexpr const uint64_t ODD_BITS = 0xAAAAAAAAAAAAAAAAULL; +private: + static constexpr const uint64_t ODD_BITS = 0xAAAAAAAAAAAAAAAAULL; - simdjson_really_inline uint64_t next_escaped_without_backslashes() noexcept { - uint64_t escaped = this->next_is_escaped; - this->next_is_escaped = 0; - return escaped; - } + simdjson_really_inline uint64_t next_escaped_without_backslashes() noexcept { + uint64_t escaped = this->next_is_escaped; + this->next_is_escaped = 0; + return escaped; + } - /** - * Returns a mask of the next escape characters (masking out escaped backslashes), along with - * any non-backslash escape codes. - * - * \n \\n \\\n \\\\n returns: - * \n \ \ \n \ \ - * 11 100 1011 10100 - * - * You are expected to mask out the first bit yourself if the previous block had a trailing - * escape. - * - * & the result with potential_escape to get just the escape characters. - * ^ the result with (potential_escape | first_is_escaped) to get escaped characters. - */ - static simdjson_really_inline uint64_t next_escape_and_terminal_code(uint64_t potential_escape) noexcept { - // If we were to just shift and mask out any odd bits, we'd actually get a *half* right answer: - // any even-aligned backslash runs would be correct! Odd-aligned backslash runs would be - // inverted (\\\ would be 010 instead of 101). - // - // ``` - // string: | ____\\\\_\\\\_____ | - // maybe_escaped | ODD | \ \ \ \ | - // even-aligned ^^^ ^^^^ odd-aligned - // ``` - // - // Taking that into account, our basic strategy is: - // - // 1. Use subtraction to produce a mask with 1's for even-aligned runs and 0's for - // odd-aligned runs. - // 2. XOR all odd bits, which masks out the odd bits in even-aligned runs, and brings IN the - // odd bits in odd-aligned runs. - // 3. & with backslash to clean up any stray bits. - // runs are set to 0, and then XORing with "odd": - // - // | | Mask (shows characters instead of 1's) | Instructions | - // |--------------------------------|----------------------------------------|---------------------| - // | string | `\\n_\\\n___\\\n___\\\\___\\\\__\\\` | - // | | ` even odd even odd odd` | - // | maybe_escaped | ` n \\n \\n \\\_ \\\_ \\` X | 1 (potential_escape << 1) - // | maybe_escaped_and_odd | ` \n_ \\n _ \\\n_ _ \\\__ _\\\_ \\\` | 1 (maybe_escaped | odd) - // | even_series_codes_and_odd | ` n_\\\ _ n_ _\\\\ _ _ ` | 1 (maybe_escaped_and_odd - potential_escape) - // | escape_and_terminal_code | ` \n \ \n \ \n \ \ \ \ \ \` | 1 (^ odd) - // + /** + * Returns a mask of the next escape characters (masking out escaped backslashes), along with + * any non-backslash escape codes. + * + * \n \\n \\\n \\\\n returns: + * \n \ \ \n \ \ + * 11 100 1011 10100 + * + * You are expected to mask out the first bit yourself if the previous block had a trailing + * escape. + * + * & the result with potential_escape to get just the escape characters. + * ^ the result with (potential_escape | first_is_escaped) to get escaped characters. + */ + static simdjson_really_inline uint64_t next_escape_and_terminal_code(uint64_t potential_escape) noexcept { + // If we were to just shift and mask out any odd bits, we'd actually get a *half* right answer: + // any even-aligned backslash runs would be correct! Odd-aligned backslash runs would be + // inverted (\\\ would be 010 instead of 101). + // + // ``` + // string: | ____\\\\_\\\\_____ | + // maybe_escaped | ODD | \ \ \ \ | + // even-aligned ^^^ ^^^^ odd-aligned + // ``` + // + // Taking that into account, our basic strategy is: + // + // 1. Use subtraction to produce a mask with 1's for even-aligned runs and 0's for + // odd-aligned runs. + // 2. XOR all odd bits, which masks out the odd bits in even-aligned runs, and brings IN the + // odd bits in odd-aligned runs. + // 3. & with backslash to clean up any stray bits. + // runs are set to 0, and then XORing with "odd": + // + // | | Mask (shows characters instead of 1's) | Instructions | + // |--------------------------------|----------------------------------------|---------------------| + // | string | `\\n_\\\n___\\\n___\\\\___\\\\__\\\` | + // | | ` even odd even odd odd` | + // | maybe_escaped | ` n \\n \\n \\\_ \\\_ \\` X | 1 (potential_escape << 1) + // | maybe_escaped_and_odd | ` \n_ \\n _ \\\n_ _ \\\__ _\\\_ \\\` | 1 (maybe_escaped | odd) + // | even_series_codes_and_odd | ` n_\\\ _ n_ _\\\\ _ _ ` | 1 (maybe_escaped_and_odd - potential_escape) + // | escape_and_terminal_code | ` \n \ \n \ \n \ \ \ \ \ \` | 1 (^ odd) + // - // Escaped characters are characters following an escape. - uint64_t maybe_escaped = potential_escape << 1; + // Escaped characters are characters following an escape. + uint64_t maybe_escaped = potential_escape << 1; - // To distinguish odd from even escape sequences, therefore, we turn on any *starting* - // escapes that are on an odd byte. (We actually bring in all odd bits, for speed.) - // - Odd runs of backslashes are 0000, and the code at the end ("n" in \n or \\n) is 1. - // - Odd runs of backslashes are 1111, and the code at the end ("n" in \n or \\n) is 0. - // - All other odd bytes are 1, and even bytes are 0. - uint64_t maybe_escaped_and_odd_bits = maybe_escaped | ODD_BITS; - uint64_t even_series_codes_and_odd_bits = maybe_escaped_and_odd_bits - potential_escape; + // To distinguish odd from even escape sequences, therefore, we turn on any *starting* + // escapes that are on an odd byte. (We actually bring in all odd bits, for speed.) + // - Odd runs of backslashes are 0000, and the code at the end ("n" in \n or \\n) is 1. + // - Odd runs of backslashes are 1111, and the code at the end ("n" in \n or \\n) is 0. + // - All other odd bytes are 1, and even bytes are 0. + uint64_t maybe_escaped_and_odd_bits = maybe_escaped | ODD_BITS; + uint64_t even_series_codes_and_odd_bits = maybe_escaped_and_odd_bits - potential_escape; - // Now we flip all odd bytes back with xor. This: - // - Makes odd runs of backslashes go from 0000 to 1010 - // - Makes even runs of backslashes go from 1111 to 1010 - // - Sets actually-escaped codes to 1 (the n in \n and \\n: \n = 11, \\n = 100) - // - Resets all other bytes to 0 - return even_series_codes_and_odd_bits ^ ODD_BITS; - } - }; + // Now we flip all odd bytes back with xor. This: + // - Makes odd runs of backslashes go from 0000 to 1010 + // - Makes even runs of backslashes go from 1111 to 1010 + // - Sets actually-escaped codes to 1 (the n in \n and \\n: \n = 11, \\n = 100) + // - Resets all other bytes to 0 + return even_series_codes_and_odd_bits ^ ODD_BITS; + } +}; - } // namespace stage1 - } // unnamed namespace - } // namespace westmere +} // namespace stage1 +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRING_SCANNER_H @@ -37749,93 +39533,93 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { - namespace stage1 { +namespace westmere { +namespace { +namespace stage1 { - struct json_string_block { - // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 - simdjson_really_inline json_string_block(uint64_t escaped, uint64_t quote, uint64_t in_string) : - _escaped(escaped), _quote(quote), _in_string(in_string) {} +struct json_string_block { + // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 + simdjson_really_inline json_string_block(uint64_t escaped, uint64_t quote, uint64_t in_string) : + _escaped(escaped), _quote(quote), _in_string(in_string) {} - // Escaped characters (characters following an escape() character) - simdjson_really_inline uint64_t escaped() const { return _escaped; } - // Real (non-backslashed) quotes - simdjson_really_inline uint64_t quote() const { return _quote; } - // Only characters inside the string (not including the quotes) - simdjson_really_inline uint64_t string_content() const { return _in_string & ~_quote; } - // Return a mask of whether the given characters are inside a string (only works on non-quotes) - simdjson_really_inline uint64_t non_quote_inside_string(uint64_t mask) const { return mask & _in_string; } - // Return a mask of whether the given characters are inside a string (only works on non-quotes) - simdjson_really_inline uint64_t non_quote_outside_string(uint64_t mask) const { return mask & ~_in_string; } - // Tail of string (everything except the start quote) - simdjson_really_inline uint64_t string_tail() const { return _in_string ^ _quote; } + // Escaped characters (characters following an escape() character) + simdjson_really_inline uint64_t escaped() const { return _escaped; } + // Real (non-backslashed) quotes + simdjson_really_inline uint64_t quote() const { return _quote; } + // Only characters inside the string (not including the quotes) + simdjson_really_inline uint64_t string_content() const { return _in_string & ~_quote; } + // Return a mask of whether the given characters are inside a string (only works on non-quotes) + simdjson_really_inline uint64_t non_quote_inside_string(uint64_t mask) const { return mask & _in_string; } + // Return a mask of whether the given characters are inside a string (only works on non-quotes) + simdjson_really_inline uint64_t non_quote_outside_string(uint64_t mask) const { return mask & ~_in_string; } + // Tail of string (everything except the start quote) + simdjson_really_inline uint64_t string_tail() const { return _in_string ^ _quote; } - // escaped characters (backslashed--does not include the hex characters after \u) - uint64_t _escaped; - // real quotes (non-escaped ones) - uint64_t _quote; - // string characters (includes start quote but not end quote) - uint64_t _in_string; - }; + // escaped characters (backslashed--does not include the hex characters after \u) + uint64_t _escaped; + // real quotes (non-escaped ones) + uint64_t _quote; + // string characters (includes start quote but not end quote) + uint64_t _in_string; +}; - // Scans blocks for string characters, storing the state necessary to do so - class json_string_scanner { - public: - simdjson_really_inline json_string_block next(const simd::simd8x64& in); - // Returns either UNCLOSED_STRING or SUCCESS - simdjson_really_inline error_code finish(); +// Scans blocks for string characters, storing the state necessary to do so +class json_string_scanner { +public: + simdjson_really_inline json_string_block next(const simd::simd8x64& in); + // Returns either UNCLOSED_STRING or SUCCESS + simdjson_really_inline error_code finish(); - private: - // Scans for escape characters - json_escape_scanner escape_scanner{}; - // Whether the last iteration was still inside a string (all 1's = true, all 0's = false). - uint64_t prev_in_string = 0ULL; - }; +private: + // Scans for escape characters + json_escape_scanner escape_scanner{}; + // Whether the last iteration was still inside a string (all 1's = true, all 0's = false). + uint64_t prev_in_string = 0ULL; +}; - // - // Return a mask of all string characters plus end quotes. - // - // prev_escaped is overflow saying whether the next character is escaped. - // prev_in_string is overflow saying whether we're still in a string. - // - // Backslash sequences outside of quotes will be detected in stage 2. - // - simdjson_really_inline json_string_block json_string_scanner::next(const simd::simd8x64& in) { - const uint64_t backslash = in.eq('\\'); - const uint64_t escaped = escape_scanner.next(backslash).escaped; - const uint64_t quote = in.eq('"') & ~escaped; +// +// Return a mask of all string characters plus end quotes. +// +// prev_escaped is overflow saying whether the next character is escaped. +// prev_in_string is overflow saying whether we're still in a string. +// +// Backslash sequences outside of quotes will be detected in stage 2. +// +simdjson_really_inline json_string_block json_string_scanner::next(const simd::simd8x64& in) { + const uint64_t backslash = in.eq('\\'); + const uint64_t escaped = escape_scanner.next(backslash).escaped; + const uint64_t quote = in.eq('"') & ~escaped; - // - // prefix_xor flips on bits inside the string (and flips off the end quote). - // - // Then we xor with prev_in_string: if we were in a string already, its effect is flipped - // (characters inside strings are outside, and characters outside strings are inside). - // - const uint64_t in_string = prefix_xor(quote) ^ prev_in_string; + // + // prefix_xor flips on bits inside the string (and flips off the end quote). + // + // Then we xor with prev_in_string: if we were in a string already, its effect is flipped + // (characters inside strings are outside, and characters outside strings are inside). + // + const uint64_t in_string = prefix_xor(quote) ^ prev_in_string; - // - // Check if we're still in a string at the end of the box so the next block will know - // - prev_in_string = uint64_t(static_cast(in_string) >> 63); + // + // Check if we're still in a string at the end of the box so the next block will know + // + prev_in_string = uint64_t(static_cast(in_string) >> 63); - // Use ^ to turn the beginning quote off, and the end quote on. + // Use ^ to turn the beginning quote off, and the end quote on. - // We are returning a function-local object so either we get a move constructor - // or we get copy elision. - return json_string_block(escaped, quote, in_string); - } + // We are returning a function-local object so either we get a move constructor + // or we get copy elision. + return json_string_block(escaped, quote, in_string); +} - simdjson_really_inline error_code json_string_scanner::finish() { - if (prev_in_string) { - return UNCLOSED_STRING; - } - return SUCCESS; - } +simdjson_really_inline error_code json_string_scanner::finish() { + if (prev_in_string) { + return UNCLOSED_STRING; + } + return SUCCESS; +} - } // namespace stage1 - } // unnamed namespace - } // namespace westmere +} // namespace stage1 +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRING_SCANNER_H @@ -37851,206 +39635,203 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { - namespace utf8_validation { +namespace westmere { +namespace { +namespace utf8_validation { - using namespace simd; +using namespace simd; - simdjson_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { - // Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) - // Bit 1 = Too Long (ASCII followed by continuation) - // Bit 2 = Overlong 3-byte - // Bit 4 = Surrogate - // Bit 5 = Overlong 2-byte - // Bit 7 = Two Continuations - constexpr const uint8_t TOO_SHORT = 1 << 0; // 11______ 0_______ - // 11______ 11______ - constexpr const uint8_t TOO_LONG = 1 << 1; // 0_______ 10______ - constexpr const uint8_t OVERLONG_3 = 1 << 2; // 11100000 100_____ - constexpr const uint8_t SURROGATE = 1 << 4; // 11101101 101_____ - constexpr const uint8_t OVERLONG_2 = 1 << 5; // 1100000_ 10______ - constexpr const uint8_t TWO_CONTS = 1 << 7; // 10______ 10______ - constexpr const uint8_t TOO_LARGE = 1 << 3; // 11110100 1001____ - // 11110100 101_____ - // 11110101 1001____ - // 11110101 101_____ - // 1111011_ 1001____ - // 1111011_ 101_____ - // 11111___ 1001____ - // 11111___ 101_____ - constexpr const uint8_t TOO_LARGE_1000 = 1 << 6; - // 11110101 1000____ - // 1111011_ 1000____ - // 11111___ 1000____ - constexpr const uint8_t OVERLONG_4 = 1 << 6; // 11110000 1000____ + simdjson_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { +// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) +// Bit 1 = Too Long (ASCII followed by continuation) +// Bit 2 = Overlong 3-byte +// Bit 4 = Surrogate +// Bit 5 = Overlong 2-byte +// Bit 7 = Two Continuations + constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______ + // 11______ 11______ + constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______ + constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____ + constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____ + constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______ + constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______ + constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____ + // 11110100 101_____ + // 11110101 1001____ + // 11110101 101_____ + // 1111011_ 1001____ + // 1111011_ 101_____ + // 11111___ 1001____ + // 11111___ 101_____ + constexpr const uint8_t TOO_LARGE_1000 = 1<<6; + // 11110101 1000____ + // 1111011_ 1000____ + // 11111___ 1000____ + constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____ - const simd8 byte_1_high = prev1.shr<4>().lookup_16( - // 0_______ ________ - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - // 10______ ________ - TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, - // 1100____ ________ - TOO_SHORT | OVERLONG_2, - // 1101____ ________ - TOO_SHORT, - // 1110____ ________ - TOO_SHORT | OVERLONG_3 | SURROGATE, - // 1111____ ________ - TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 - ); - constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . - const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( - // ____0000 ________ - CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, - // ____0001 ________ - CARRY | OVERLONG_2, - // ____001_ ________ - CARRY, - CARRY, + const simd8 byte_1_high = prev1.shr<4>().lookup_16( + // 0_______ ________ + TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, + TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, + // 10______ ________ + TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, + // 1100____ ________ + TOO_SHORT | OVERLONG_2, + // 1101____ ________ + TOO_SHORT, + // 1110____ ________ + TOO_SHORT | OVERLONG_3 | SURROGATE, + // 1111____ ________ + TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 + ); + constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . + const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( + // ____0000 ________ + CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, + // ____0001 ________ + CARRY | OVERLONG_2, + // ____001_ ________ + CARRY, + CARRY, - // ____0100 ________ - CARRY | TOO_LARGE, - // ____0101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____011_ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, + // ____0100 ________ + CARRY | TOO_LARGE, + // ____0101 ________ + CARRY | TOO_LARGE | TOO_LARGE_1000, + // ____011_ ________ + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____1___ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____1101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000 - ); - const simd8 byte_2_high = input.shr<4>().lookup_16( - // ________ 0_______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, + // ____1___ ________ + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + // ____1101 ________ + CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000 + ); + const simd8 byte_2_high = input.shr<4>().lookup_16( + // ________ 0_______ + TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, + TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - // ________ 1000____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, - // ________ 1001____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, - // ________ 101_____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, + // ________ 1000____ + TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, + // ________ 1001____ + TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, + // ________ 101_____ + TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, + TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - // ________ 11______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT - ); - return (byte_1_high & byte_1_low & byte_2_high); - } - simdjson_inline simd8 check_multibyte_lengths(const simd8 input, - const simd8 prev_input, const simd8 sc) { - simd8 prev2 = input.prev<2>(prev_input); - simd8 prev3 = input.prev<3>(prev_input); - simd8 must23 = must_be_2_3_continuation(prev2, prev3); - simd8 must23_80 = must23 & uint8_t(0x80); - return must23_80 ^ sc; - } + // ________ 11______ + TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT + ); + return (byte_1_high & byte_1_low & byte_2_high); + } + simdjson_inline simd8 check_multibyte_lengths(const simd8 input, + const simd8 prev_input, const simd8 sc) { + simd8 prev2 = input.prev<2>(prev_input); + simd8 prev3 = input.prev<3>(prev_input); + simd8 must23 = must_be_2_3_continuation(prev2, prev3); + simd8 must23_80 = must23 & uint8_t(0x80); + return must23_80 ^ sc; + } - // - // Return nonzero if there are incomplete multibyte characters at the end of the block: - // e.g. if there is a 4-byte character, but it's 3 bytes from the end. - // - simdjson_inline simd8 is_incomplete(const simd8 input) { - // If the previous input's last 3 bytes match this, they're too short (they ended at EOF): - // ... 1111____ 111_____ 11______ -#if SIMDJSON_IMPLEMENTATION_ICELAKE - static const uint8_t max_array[64] = { - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0xf0u - 1, 0xe0u - 1, 0xc0u - 1 - }; + // + // Return nonzero if there are incomplete multibyte characters at the end of the block: + // e.g. if there is a 4-byte character, but it's 3 bytes from the end. + // + simdjson_inline simd8 is_incomplete(const simd8 input) { + // If the previous input's last 3 bytes match this, they're too short (they ended at EOF): + // ... 1111____ 111_____ 11______ +#if SIMDJSON_IMPLEMENTATION_ICELAKE || (SIMDJSON_IMPLEMENTATION_RVV_VLS && __riscv_v_fixed_vlen >= 512) + static const uint8_t max_array[64] = { + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0xf0u-1, 0xe0u-1, 0xc0u-1 + }; #else - static const uint8_t max_array[32] = { - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0xf0u - 1, 0xe0u - 1, 0xc0u - 1 - }; + static const uint8_t max_array[32] = { + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0xf0u-1, 0xe0u-1, 0xc0u-1 + }; #endif - const simd8 max_value(&max_array[sizeof(max_array) - sizeof(simd8)]); - return input.gt_bits(max_value); - } + const simd8 max_value(&max_array[sizeof(max_array)-sizeof(simd8)]); + return input.gt_bits(max_value); + } - struct utf8_checker { - // If this is nonzero, there has been a UTF-8 error. - simd8 error; - // The last input we received - simd8 prev_input_block; - // Whether the last input we received was incomplete (used for ASCII fast path) - simd8 prev_incomplete; + struct utf8_checker { + // If this is nonzero, there has been a UTF-8 error. + simd8 error; + // The last input we received + simd8 prev_input_block; + // Whether the last input we received was incomplete (used for ASCII fast path) + simd8 prev_incomplete; - // - // Check whether the current bytes are valid UTF-8. - // - simdjson_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { - // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes - // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) - simd8 prev1 = input.prev<1>(prev_input); - simd8 sc = check_special_cases(input, prev1); - this->error |= check_multibyte_lengths(input, prev_input, sc); - } + // + // Check whether the current bytes are valid UTF-8. + // + simdjson_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { + // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes + // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) + simd8 prev1 = input.prev<1>(prev_input); + simd8 sc = check_special_cases(input, prev1); + this->error |= check_multibyte_lengths(input, prev_input, sc); + } - // The only problem that can happen at EOF is that a multibyte character is too short - // or a byte value too large in the last bytes: check_special_cases only checks for bytes - // too large in the first of two bytes. - simdjson_inline void check_eof() { - // If the previous block had incomplete UTF-8 characters at the end, an ASCII block can't - // possibly finish them. - this->error |= this->prev_incomplete; - } + // The only problem that can happen at EOF is that a multibyte character is too short + // or a byte value too large in the last bytes: check_special_cases only checks for bytes + // too large in the first of two bytes. + simdjson_inline void check_eof() { + // If the previous block had incomplete UTF-8 characters at the end, an ASCII block can't + // possibly finish them. + this->error |= this->prev_incomplete; + } - simdjson_inline void check_next_input(const simd8x64& input) { - if (simdjson_likely(is_ascii(input))) { - this->error |= this->prev_incomplete; - } - else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 1) - || (simd8x64::NUM_CHUNKS == 2) - || (simd8x64::NUM_CHUNKS == 4), - "We support one, two or four chunks per 64-byte block."); - SIMDJSON_IF_CONSTEXPR(simd8x64::NUM_CHUNKS == 1) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - } -else SIMDJSON_IF_CONSTEXPR(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); + simdjson_inline void check_next_input(const simd8x64& input) { + if(simdjson_likely(is_ascii(input))) { + this->error |= this->prev_incomplete; + } else { + // you might think that a for-loop would work, but under Visual Studio, it is not good enough. + static_assert((simd8x64::NUM_CHUNKS == 1) + ||(simd8x64::NUM_CHUNKS == 2) + || (simd8x64::NUM_CHUNKS == 4), + "We support one, two or four chunks per 64-byte block."); + SIMDJSON_IF_CONSTEXPR (simd8x64::NUM_CHUNKS == 1) { + this->check_utf8_bytes(input.get<0>(), this->prev_input_block); + } else SIMDJSON_IF_CONSTEXPR (simd8x64::NUM_CHUNKS == 2) { + this->check_utf8_bytes(input.get<0>(), this->prev_input_block); + this->check_utf8_bytes(input.get<1>(), input.get<0>()); + } else SIMDJSON_IF_CONSTEXPR (simd8x64::NUM_CHUNKS == 4) { + this->check_utf8_bytes(input.get<0>(), this->prev_input_block); + this->check_utf8_bytes(input.get<1>(), input.get<0>()); + this->check_utf8_bytes(input.get<2>(), input.get<1>()); + this->check_utf8_bytes(input.get<3>(), input.get<2>()); } - else SIMDJSON_IF_CONSTEXPR(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - this->prev_incomplete = is_incomplete(input.chunks[simd8x64::NUM_CHUNKS - 1]); - this->prev_input_block = input.chunks[simd8x64::NUM_CHUNKS - 1]; - } - } - // do not forget to call check_eof! - simdjson_inline error_code errors() { - return this->error.any_bits_set_anywhere() ? error_code::UTF8_ERROR : error_code::SUCCESS; - } + this->prev_incomplete = is_incomplete(input.get::NUM_CHUNKS-1>()); + this->prev_input_block = input.get::NUM_CHUNKS-1>(); + } + } + // do not forget to call check_eof! + simdjson_warn_unused simdjson_inline error_code errors() { + return this->error.any_bits_set_anywhere() ? error_code::UTF8_ERROR : error_code::SUCCESS; + } - }; // struct utf8_checker - } // namespace utf8_validation + }; // struct utf8_checker +} // namespace utf8_validation - } // unnamed namespace - } // namespace westmere +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_UTF8_LOOKUP4_ALGORITHM_H @@ -38067,161 +39848,161 @@ else SIMDJSON_IF_CONSTEXPR(simd8x64::NUM_CHUNKS == 2) { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { - namespace stage1 { +namespace westmere { +namespace { +namespace stage1 { - /** - * A block of scanned json, with information on operators and scalars. - * - * We seek to identify pseudo-structural characters. Anything that is inside - * a string must be omitted (hence & ~_string.string_tail()). - * Otherwise, pseudo-structural characters come in two forms. - * 1. We have the structural characters ([,],{,},:, comma). The - * term 'structural character' is from the JSON RFC. - * 2. We have the 'scalar pseudo-structural characters'. - * Scalars are quotes, and any character except structural characters and white space. - * - * To identify the scalar pseudo-structural characters, we must look at what comes - * before them: it must be a space, a quote or a structural characters. - * Starting with simdjson v0.3, we identify them by - * negation: we identify everything that is followed by a non-quote scalar, - * and we negate that. Whatever remains must be a 'scalar pseudo-structural character'. - */ - struct json_block { - public: - // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 - simdjson_inline json_block(json_string_block&& string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : - _string(std::move(string)), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} - simdjson_inline json_block(json_string_block string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : - _string(string), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} +/** + * A block of scanned json, with information on operators and scalars. + * + * We seek to identify pseudo-structural characters. Anything that is inside + * a string must be omitted (hence & ~_string.string_tail()). + * Otherwise, pseudo-structural characters come in two forms. + * 1. We have the structural characters ([,],{,},:, comma). The + * term 'structural character' is from the JSON RFC. + * 2. We have the 'scalar pseudo-structural characters'. + * Scalars are quotes, and any character except structural characters and white space. + * + * To identify the scalar pseudo-structural characters, we must look at what comes + * before them: it must be a space, a quote or a structural characters. + * Starting with simdjson v0.3, we identify them by + * negation: we identify everything that is followed by a non-quote scalar, + * and we negate that. Whatever remains must be a 'scalar pseudo-structural character'. + */ +struct json_block { +public: + // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 + simdjson_inline json_block(json_string_block&& string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : + _string(std::move(string)), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} + simdjson_inline json_block(json_string_block string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : + _string(string), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} - /** - * The start of structurals. - * In simdjson prior to v0.3, these were called the pseudo-structural characters. - **/ - simdjson_inline uint64_t structural_start() const noexcept { return potential_structural_start() & ~_string.string_tail(); } - /** All JSON whitespace (i.e. not in a string) */ - simdjson_inline uint64_t whitespace() const noexcept { return non_quote_outside_string(_characters.whitespace()); } + /** + * The start of structurals. + * In simdjson prior to v0.3, these were called the pseudo-structural characters. + **/ + simdjson_inline uint64_t structural_start() const noexcept { return potential_structural_start() & ~_string.string_tail(); } + /** All JSON whitespace (i.e. not in a string) */ + simdjson_inline uint64_t whitespace() const noexcept { return non_quote_outside_string(_characters.whitespace()); } - // Helpers + // Helpers - /** Whether the given characters are inside a string (only works on non-quotes) */ - simdjson_inline uint64_t non_quote_inside_string(uint64_t mask) const noexcept { return _string.non_quote_inside_string(mask); } - /** Whether the given characters are outside a string (only works on non-quotes) */ - simdjson_inline uint64_t non_quote_outside_string(uint64_t mask) const noexcept { return _string.non_quote_outside_string(mask); } + /** Whether the given characters are inside a string (only works on non-quotes) */ + simdjson_inline uint64_t non_quote_inside_string(uint64_t mask) const noexcept { return _string.non_quote_inside_string(mask); } + /** Whether the given characters are outside a string (only works on non-quotes) */ + simdjson_inline uint64_t non_quote_outside_string(uint64_t mask) const noexcept { return _string.non_quote_outside_string(mask); } - // string and escape characters - json_string_block _string; - // whitespace, structural characters ('operators'), scalars - json_character_block _characters; - // whether the previous character was a scalar - uint64_t _follows_potential_nonquote_scalar; - private: - // Potential structurals (i.e. disregarding strings) + // string and escape characters + json_string_block _string; + // whitespace, structural characters ('operators'), scalars + json_character_block _characters; + // whether the previous character was a scalar + uint64_t _follows_potential_nonquote_scalar; +private: + // Potential structurals (i.e. disregarding strings) - /** - * structural elements ([,],{,},:, comma) plus scalar starts like 123, true and "abc". - * They may reside inside a string. - **/ - simdjson_inline uint64_t potential_structural_start() const noexcept { return _characters.op() | potential_scalar_start(); } - /** - * The start of non-operator runs, like 123, true and "abc". - * It main reside inside a string. - **/ - simdjson_inline uint64_t potential_scalar_start() const noexcept { - // The term "scalar" refers to anything except structural characters and white space - // (so letters, numbers, quotes). - // Whenever it is preceded by something that is not a structural element ({,},[,],:, ") nor a white-space - // then we know that it is irrelevant structurally. - return _characters.scalar() & ~follows_potential_scalar(); - } - /** - * Whether the given character is immediately after a non-operator like 123, true. - * The characters following a quote are not included. - */ - simdjson_inline uint64_t follows_potential_scalar() const noexcept { - // _follows_potential_nonquote_scalar: is defined as marking any character that follows a character - // that is not a structural element ({,},[,],:, comma) nor a quote (") and that is not a - // white space. - // It is understood that within quoted region, anything at all could be marked (irrelevant). - return _follows_potential_nonquote_scalar; - } - }; + /** + * structural elements ([,],{,},:, comma) plus scalar starts like 123, true and "abc". + * They may reside inside a string. + **/ + simdjson_inline uint64_t potential_structural_start() const noexcept { return _characters.op() | potential_scalar_start(); } + /** + * The start of non-operator runs, like 123, true and "abc". + * It main reside inside a string. + **/ + simdjson_inline uint64_t potential_scalar_start() const noexcept { + // The term "scalar" refers to anything except structural characters and white space + // (so letters, numbers, quotes). + // Whenever it is preceded by something that is not a structural element ({,},[,],:, ") nor a white-space + // then we know that it is irrelevant structurally. + return _characters.scalar() & ~follows_potential_scalar(); + } + /** + * Whether the given character is immediately after a non-operator like 123, true. + * The characters following a quote are not included. + */ + simdjson_inline uint64_t follows_potential_scalar() const noexcept { + // _follows_potential_nonquote_scalar: is defined as marking any character that follows a character + // that is not a structural element ({,},[,],:, comma) nor a quote (") and that is not a + // white space. + // It is understood that within quoted region, anything at all could be marked (irrelevant). + return _follows_potential_nonquote_scalar; + } +}; - /** - * Scans JSON for important bits: structural characters or 'operators', strings, and scalars. - * - * The scanner starts by calculating two distinct things: - * - string characters (taking \" into account) - * - structural characters or 'operators' ([]{},:, comma) - * and scalars (runs of non-operators like 123, true and "abc") - * - * To minimize data dependency (a key component of the scanner's speed), it finds these in parallel: - * in particular, the operator/scalar bit will find plenty of things that are actually part of - * strings. When we're done, json_block will fuse the two together by masking out tokens that are - * part of a string. - */ - class json_scanner { - public: - json_scanner() = default; - simdjson_inline json_block next(const simd::simd8x64& in); - // Returns either UNCLOSED_STRING or SUCCESS - simdjson_inline error_code finish(); +/** + * Scans JSON for important bits: structural characters or 'operators', strings, and scalars. + * + * The scanner starts by calculating two distinct things: + * - string characters (taking \" into account) + * - structural characters or 'operators' ([]{},:, comma) + * and scalars (runs of non-operators like 123, true and "abc") + * + * To minimize data dependency (a key component of the scanner's speed), it finds these in parallel: + * in particular, the operator/scalar bit will find plenty of things that are actually part of + * strings. When we're done, json_block will fuse the two together by masking out tokens that are + * part of a string. + */ +class json_scanner { +public: + json_scanner() = default; + simdjson_inline json_block next(const simd::simd8x64& in); + // Returns either UNCLOSED_STRING or SUCCESS + simdjson_warn_unused simdjson_inline error_code finish(); - private: - // Whether the last character of the previous iteration is part of a scalar token - // (anything except whitespace or a structural character/'operator'). - uint64_t prev_scalar = 0ULL; - json_string_scanner string_scanner{}; - }; +private: + // Whether the last character of the previous iteration is part of a scalar token + // (anything except whitespace or a structural character/'operator'). + uint64_t prev_scalar = 0ULL; + json_string_scanner string_scanner{}; +}; - // - // Check if the current character immediately follows a matching character. - // - // For example, this checks for quotes with backslashes in front of them: - // - // const uint64_t backslashed_quote = in.eq('"') & immediately_follows(in.eq('\'), prev_backslash); - // - simdjson_inline uint64_t follows(const uint64_t match, uint64_t& overflow) { - const uint64_t result = match << 1 | overflow; - overflow = match >> 63; - return result; - } +// +// Check if the current character immediately follows a matching character. +// +// For example, this checks for quotes with backslashes in front of them: +// +// const uint64_t backslashed_quote = in.eq('"') & immediately_follows(in.eq('\'), prev_backslash); +// +simdjson_inline uint64_t follows(const uint64_t match, uint64_t &overflow) { + const uint64_t result = match << 1 | overflow; + overflow = match >> 63; + return result; +} - simdjson_inline json_block json_scanner::next(const simd::simd8x64& in) { - json_string_block strings = string_scanner.next(in); - // identifies the white-space and the structural characters - json_character_block characters = json_character_block::classify(in); - // The term "scalar" refers to anything except structural characters and white space - // (so letters, numbers, quotes). - // We want follows_scalar to mark anything that follows a non-quote scalar (so letters and numbers). - // - // A terminal quote should either be followed by a structural character (comma, brace, bracket, colon) - // or nothing. However, we still want ' "a string"true ' to mark the 't' of 'true' as a potential - // pseudo-structural character just like we would if we had ' "a string" true '; otherwise we - // may need to add an extra check when parsing strings. - // - // Performance: there are many ways to skin this cat. - const uint64_t nonquote_scalar = characters.scalar() & ~strings.quote(); - uint64_t follows_nonquote_scalar = follows(nonquote_scalar, prev_scalar); - // We are returning a function-local object so either we get a move constructor - // or we get copy elision. - return json_block( - strings,// strings is a function-local object so either it moves or the copy is elided. - characters, - follows_nonquote_scalar - ); - } +simdjson_inline json_block json_scanner::next(const simd::simd8x64& in) { + json_string_block strings = string_scanner.next(in); + // identifies the white-space and the structural characters + json_character_block characters = json_character_block::classify(in); + // The term "scalar" refers to anything except structural characters and white space + // (so letters, numbers, quotes). + // We want follows_scalar to mark anything that follows a non-quote scalar (so letters and numbers). + // + // A terminal quote should either be followed by a structural character (comma, brace, bracket, colon) + // or nothing. However, we still want ' "a string"true ' to mark the 't' of 'true' as a potential + // pseudo-structural character just like we would if we had ' "a string" true '; otherwise we + // may need to add an extra check when parsing strings. + // + // Performance: there are many ways to skin this cat. + const uint64_t nonquote_scalar = characters.scalar() & ~strings.quote(); + uint64_t follows_nonquote_scalar = follows(nonquote_scalar, prev_scalar); + // We are returning a function-local object so either we get a move constructor + // or we get copy elision. + return json_block( + strings,// strings is a function-local object so either it moves or the copy is elided. + characters, + follows_nonquote_scalar + ); +} - simdjson_inline error_code json_scanner::finish() { - return string_scanner.finish(); - } +simdjson_warn_unused simdjson_inline error_code json_scanner::finish() { + return string_scanner.finish(); +} - } // namespace stage1 - } // unnamed namespace - } // namespace westmere +} // namespace stage1 +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_SCANNER_H @@ -38239,99 +40020,99 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { - namespace stage1 { +namespace westmere { +namespace { +namespace stage1 { - /** - * This algorithm is used to quickly identify the last structural position that - * makes up a complete document. - * - * It does this by going backwards and finding the last *document boundary* (a - * place where one value follows another without a comma between them). If the - * last document (the characters after the boundary) has an equal number of - * start and end brackets, it is considered complete. - * - * Simply put, we iterate over the structural characters, starting from - * the end. We consider that we found the end of a JSON document when the - * first element of the pair is NOT one of these characters: '{' '[' ':' ',' - * and when the second element is NOT one of these characters: '}' ']' ':' ','. - * - * This simple comparison works most of the time, but it does not cover cases - * where the batch's structural indexes contain a perfect amount of documents. - * In such a case, we do not have access to the structural index which follows - * the last document, therefore, we do not have access to the second element in - * the pair, and that means we cannot identify the last document. To fix this - * issue, we keep a count of the open and closed curly/square braces we found - * while searching for the pair. When we find a pair AND the count of open and - * closed curly/square braces is the same, we know that we just passed a - * complete document, therefore the last json buffer location is the end of the - * batch. - */ - simdjson_inline uint32_t find_next_document_index(dom_parser_implementation& parser) { - // Variant: do not count separately, just figure out depth - if (parser.n_structural_indexes == 0) { return 0; } - auto arr_cnt = 0; - auto obj_cnt = 0; - for (auto i = parser.n_structural_indexes - 1; i > 0; i--) { - auto idxb = parser.structural_indexes[i]; - switch (parser.buf[idxb]) { - case ':': - case ',': - continue; - case '}': - obj_cnt--; - continue; - case ']': - arr_cnt--; - continue; - case '{': - obj_cnt++; - break; - case '[': - arr_cnt++; - break; - } - auto idxa = parser.structural_indexes[i - 1]; - switch (parser.buf[idxa]) { - case '{': - case '[': - case ':': - case ',': - continue; - } - // Last document is complete, so the next document will appear after! - if (!arr_cnt && !obj_cnt) { - return parser.n_structural_indexes; - } - // Last document is incomplete; mark the document at i + 1 as the next one - return i; - } - // If we made it to the end, we want to finish counting to see if we have a full document. - switch (parser.buf[parser.structural_indexes[0]]) { - case '}': - obj_cnt--; - break; - case ']': - arr_cnt--; - break; - case '{': - obj_cnt++; - break; - case '[': - arr_cnt++; - break; - } - if (!arr_cnt && !obj_cnt) { - // We have a complete document. - return parser.n_structural_indexes; - } - return 0; - } +/** + * This algorithm is used to quickly identify the last structural position that + * makes up a complete document. + * + * It does this by going backwards and finding the last *document boundary* (a + * place where one value follows another without a comma between them). If the + * last document (the characters after the boundary) has an equal number of + * start and end brackets, it is considered complete. + * + * Simply put, we iterate over the structural characters, starting from + * the end. We consider that we found the end of a JSON document when the + * first element of the pair is NOT one of these characters: '{' '[' ':' ',' + * and when the second element is NOT one of these characters: '}' ']' ':' ','. + * + * This simple comparison works most of the time, but it does not cover cases + * where the batch's structural indexes contain a perfect amount of documents. + * In such a case, we do not have access to the structural index which follows + * the last document, therefore, we do not have access to the second element in + * the pair, and that means we cannot identify the last document. To fix this + * issue, we keep a count of the open and closed curly/square braces we found + * while searching for the pair. When we find a pair AND the count of open and + * closed curly/square braces is the same, we know that we just passed a + * complete document, therefore the last json buffer location is the end of the + * batch. + */ +simdjson_inline uint32_t find_next_document_index(dom_parser_implementation &parser) { + // Variant: do not count separately, just figure out depth + if(parser.n_structural_indexes == 0) { return 0; } + auto arr_cnt = 0; + auto obj_cnt = 0; + for (auto i = parser.n_structural_indexes - 1; i > 0; i--) { + auto idxb = parser.structural_indexes[i]; + switch (parser.buf[idxb]) { + case ':': + case ',': + continue; + case '}': + obj_cnt--; + continue; + case ']': + arr_cnt--; + continue; + case '{': + obj_cnt++; + break; + case '[': + arr_cnt++; + break; + } + auto idxa = parser.structural_indexes[i - 1]; + switch (parser.buf[idxa]) { + case '{': + case '[': + case ':': + case ',': + continue; + } + // Last document is complete, so the next document will appear after! + if (!arr_cnt && !obj_cnt) { + return parser.n_structural_indexes; + } + // Last document is incomplete; mark the document at i + 1 as the next one + return i; + } + // If we made it to the end, we want to finish counting to see if we have a full document. + switch (parser.buf[parser.structural_indexes[0]]) { + case '}': + obj_cnt--; + break; + case ']': + arr_cnt--; + break; + case '{': + obj_cnt++; + break; + case '[': + arr_cnt++; + break; + } + if (!arr_cnt && !obj_cnt) { + // We have a complete document. + return parser.n_structural_indexes; + } + return 0; +} - } // namespace stage1 - } // unnamed namespace - } // namespace westmere +} // namespace stage1 +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_FIND_NEXT_DOCUMENT_INDEX_H @@ -38353,92 +40134,92 @@ namespace simdjson { // "simdjson/stage1.h" (this simplifies amalgation) namespace simdjson { - namespace westmere { - namespace { - namespace stage1 { +namespace westmere { +namespace { +namespace stage1 { - class json_minifier { - public: - template - static error_code minify(const uint8_t* buf, size_t len, uint8_t* dst, size_t& dst_len) noexcept; +class json_minifier { +public: + template + static error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) noexcept; - private: - simdjson_inline json_minifier(uint8_t* _dst) - : dst{ _dst } - {} - template - simdjson_inline void step(const uint8_t* block_buf, buf_block_reader& reader) noexcept; - simdjson_inline void next(const simd::simd8x64& in, const json_block& block); - simdjson_inline error_code finish(uint8_t* dst_start, size_t& dst_len); - json_scanner scanner{}; - uint8_t* dst; - }; +private: + simdjson_inline json_minifier(uint8_t *_dst) + : dst{_dst} + {} + template + simdjson_inline void step(const uint8_t *block_buf, buf_block_reader &reader) noexcept; + simdjson_inline void next(const simd::simd8x64& in, const json_block& block); + simdjson_warn_unused simdjson_inline error_code finish(uint8_t *dst_start, size_t &dst_len); + json_scanner scanner{}; + uint8_t *dst; +}; - simdjson_inline void json_minifier::next(const simd::simd8x64& in, const json_block& block) { - uint64_t mask = block.whitespace(); - dst += in.compress(mask, dst); - } +simdjson_inline void json_minifier::next(const simd::simd8x64& in, const json_block& block) { + uint64_t mask = block.whitespace(); + dst += in.compress(mask, dst); +} - simdjson_inline error_code json_minifier::finish(uint8_t* dst_start, size_t& dst_len) { - error_code error = scanner.finish(); - if (error) { dst_len = 0; return error; } - dst_len = dst - dst_start; - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code json_minifier::finish(uint8_t *dst_start, size_t &dst_len) { + error_code error = scanner.finish(); + if (error) { dst_len = 0; return error; } + dst_len = dst - dst_start; + return SUCCESS; +} - template<> - simdjson_inline void json_minifier::step<128>(const uint8_t* block_buf, buf_block_reader<128>& reader) noexcept { - simd::simd8x64 in_1(block_buf); - simd::simd8x64 in_2(block_buf + 64); - json_block block_1 = scanner.next(in_1); - json_block block_2 = scanner.next(in_2); - this->next(in_1, block_1); - this->next(in_2, block_2); - reader.advance(); - } +template<> +simdjson_inline void json_minifier::step<128>(const uint8_t *block_buf, buf_block_reader<128> &reader) noexcept { + simd::simd8x64 in_1(block_buf); + simd::simd8x64 in_2(block_buf+64); + json_block block_1 = scanner.next(in_1); + json_block block_2 = scanner.next(in_2); + this->next(in_1, block_1); + this->next(in_2, block_2); + reader.advance(); +} - template<> - simdjson_inline void json_minifier::step<64>(const uint8_t* block_buf, buf_block_reader<64>& reader) noexcept { - simd::simd8x64 in_1(block_buf); - json_block block_1 = scanner.next(in_1); - this->next(block_buf, block_1); - reader.advance(); - } +template<> +simdjson_inline void json_minifier::step<64>(const uint8_t *block_buf, buf_block_reader<64> &reader) noexcept { + simd::simd8x64 in_1(block_buf); + json_block block_1 = scanner.next(in_1); + this->next(block_buf, block_1); + reader.advance(); +} - template - error_code json_minifier::minify(const uint8_t* buf, size_t len, uint8_t* dst, size_t& dst_len) noexcept { - buf_block_reader reader(buf, len); - json_minifier minifier(dst); +template +error_code json_minifier::minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) noexcept { + buf_block_reader reader(buf, len); + json_minifier minifier(dst); - // Index the first n-1 blocks - while (reader.has_full_block()) { - minifier.step(reader.full_block(), reader); - } + // Index the first n-1 blocks + while (reader.has_full_block()) { + minifier.step(reader.full_block(), reader); + } - // Index the last (remainder) block, padded with spaces - uint8_t block[STEP_SIZE]; - size_t remaining_bytes = reader.get_remainder(block); - if (remaining_bytes > 0) { - // We do not want to write directly to the output stream. Rather, we write - // to a local buffer (for safety). - uint8_t out_block[STEP_SIZE]; - uint8_t* const guarded_dst{ minifier.dst }; - minifier.dst = out_block; - minifier.step(block, reader); - size_t to_write = minifier.dst - out_block; - // In some cases, we could be enticed to consider the padded spaces - // as part of the string. This is fine as long as we do not write more - // than we consumed. - if (to_write > remaining_bytes) { to_write = remaining_bytes; } - memcpy(guarded_dst, out_block, to_write); - minifier.dst = guarded_dst + to_write; - } - return minifier.finish(dst, dst_len); - } + // Index the last (remainder) block, padded with spaces + uint8_t block[STEP_SIZE]; + size_t remaining_bytes = reader.get_remainder(block); + if (remaining_bytes > 0) { + // We do not want to write directly to the output stream. Rather, we write + // to a local buffer (for safety). + uint8_t out_block[STEP_SIZE]; + uint8_t * const guarded_dst{minifier.dst}; + minifier.dst = out_block; + minifier.step(block, reader); + size_t to_write = minifier.dst - out_block; + // In some cases, we could be enticed to consider the padded spaces + // as part of the string. This is fine as long as we do not write more + // than we consumed. + if(to_write > remaining_bytes) { to_write = remaining_bytes; } + memcpy(guarded_dst, out_block, to_write); + minifier.dst = guarded_dst + to_write; + } + return minifier.finish(dst, dst_len); +} - } // namespace stage1 - } // unnamed namespace - } // namespace westmere +} // namespace stage1 +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_MINIFIER_H @@ -38464,341 +40245,339 @@ namespace simdjson { // "simdjson/stage1.h" (this simplifies amalgation) namespace simdjson { - namespace westmere { - namespace { - namespace stage1 { +namespace westmere { +namespace { +namespace stage1 { - class bit_indexer { - public: - uint32_t* tail; +class bit_indexer { +public: + uint32_t *tail; - simdjson_inline bit_indexer(uint32_t* index_buf) : tail(index_buf) {} + simdjson_inline bit_indexer(uint32_t *index_buf) : tail(index_buf) {} #if SIMDJSON_PREFER_REVERSE_BITS - /** - * ARM lacks a fast trailing zero instruction, but it has a fast - * bit reversal instruction and a fast leading zero instruction. - * Thus it may be profitable to reverse the bits (once) and then - * to rely on a sequence of instructions that call the leading - * zero instruction. - * - * Performance notes: - * The chosen routine is not optimal in terms of data dependency - * since zero_leading_bit might require two instructions. However, - * it tends to minimize the total number of instructions which is - * beneficial. - */ - simdjson_inline void write_index(uint32_t idx, uint64_t& rev_bits, int i) { - int lz = leading_zeroes(rev_bits); - this->tail[i] = static_cast(idx) + lz; - rev_bits = zero_leading_bit(rev_bits, lz); - } + /** + * ARM lacks a fast trailing zero instruction, but it has a fast + * bit reversal instruction and a fast leading zero instruction. + * Thus it may be profitable to reverse the bits (once) and then + * to rely on a sequence of instructions that call the leading + * zero instruction. + * + * Performance notes: + * The chosen routine is not optimal in terms of data dependency + * since zero_leading_bit might require two instructions. However, + * it tends to minimize the total number of instructions which is + * beneficial. + */ + simdjson_inline void write_index(uint32_t idx, uint64_t& rev_bits, int i) { + int lz = leading_zeroes(rev_bits); + this->tail[i] = static_cast(idx) + lz; + rev_bits = zero_leading_bit(rev_bits, lz); + } #else - /** - * Under recent x64 systems, we often have both a fast trailing zero - * instruction and a fast 'clear-lower-bit' instruction so the following - * algorithm can be competitive. - */ + /** + * Under recent x64 systems, we often have both a fast trailing zero + * instruction and a fast 'clear-lower-bit' instruction so the following + * algorithm can be competitive. + */ - simdjson_inline void write_index(uint32_t idx, uint64_t& bits, int i) { - this->tail[i] = idx + trailing_zeroes(bits); - bits = clear_lowest_bit(bits); - } + simdjson_inline void write_index(uint32_t idx, uint64_t& bits, int i) { + this->tail[i] = idx + trailing_zeroes(bits); + bits = clear_lowest_bit(bits); + } #endif // SIMDJSON_PREFER_REVERSE_BITS - template - simdjson_inline int write_indexes(uint32_t idx, uint64_t& bits) { - write_index(idx, bits, START); - SIMDJSON_IF_CONSTEXPR(N > 1) { - write_indexes<(N - 1 > 0 ? START + 1 : START), (N - 1 >= 0 ? N - 1 : 1)>(idx, bits); - } - return START + N; - } + template + simdjson_inline int write_indexes(uint32_t idx, uint64_t& bits) { + write_index(idx, bits, START); + SIMDJSON_IF_CONSTEXPR (N > 1) { + write_indexes<(N-1>0?START+1:START), (N-1>=0?N-1:1)>(idx, bits); + } + return START+N; + } - template - simdjson_inline int write_indexes_stepped(uint32_t idx, uint64_t& bits, int cnt) { - write_indexes(idx, bits); - SIMDJSON_IF_CONSTEXPR((START + STEP) < END) { - if (simdjson_unlikely((START + STEP) < cnt)) { - write_indexes_stepped<(START + STEP < END ? START + STEP : END), END, STEP>(idx, bits, cnt); - } - } - return ((END - START) % STEP) == 0 ? END : (END - START) - ((END - START) % STEP) + STEP; - } + template + simdjson_inline int write_indexes_stepped(uint32_t idx, uint64_t& bits, int cnt) { + write_indexes(idx, bits); + SIMDJSON_IF_CONSTEXPR ((START+STEP) < END) { + if (simdjson_unlikely((START+STEP) < cnt)) { + write_indexes_stepped<(START+STEP(idx, bits, cnt); + } + } + return ((END-START) % STEP) == 0 ? END : (END-START) - ((END-START) % STEP) + STEP; + } - // flatten out values in 'bits' assuming that they are are to have values of idx - // plus their position in the bitvector, and store these indexes at - // base_ptr[base] incrementing base as we go - // will potentially store extra values beyond end of valid bits, so base_ptr - // needs to be large enough to handle this - // - // If the kernel sets SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER, then it - // will provide its own version of the code. + // flatten out values in 'bits' assuming that they are are to have values of idx + // plus their position in the bitvector, and store these indexes at + // base_ptr[base] incrementing base as we go + // will potentially store extra values beyond end of valid bits, so base_ptr + // needs to be large enough to handle this + // + // If the kernel sets SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER, then it + // will provide its own version of the code. #ifdef SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER - simdjson_inline void write(uint32_t idx, uint64_t bits); + simdjson_inline void write(uint32_t idx, uint64_t bits); #else - simdjson_inline void write(uint32_t idx, uint64_t bits) { - // In some instances, the next branch is expensive because it is mispredicted. - // Unfortunately, in other cases, - // it helps tremendously. - if (bits == 0) - return; + simdjson_inline void write(uint32_t idx, uint64_t bits) { + // In some instances, the next branch is expensive because it is mispredicted. + // Unfortunately, in other cases, + // it helps tremendously. + if (bits == 0) + return; - int cnt = static_cast(count_ones(bits)); + int cnt = static_cast(count_ones(bits)); #if SIMDJSON_PREFER_REVERSE_BITS - bits = reverse_bits(bits); + bits = reverse_bits(bits); #endif #ifdef SIMDJSON_STRUCTURAL_INDEXER_STEP - static constexpr const int STEP = SIMDJSON_STRUCTURAL_INDEXER_STEP; + static constexpr const int STEP = SIMDJSON_STRUCTURAL_INDEXER_STEP; #else - static constexpr const int STEP = 4; + static constexpr const int STEP = 4; #endif - static constexpr const int STEP_UNTIL = 24; + static constexpr const int STEP_UNTIL = 24; - write_indexes_stepped<0, STEP_UNTIL, STEP>(idx, bits, cnt); - SIMDJSON_IF_CONSTEXPR(STEP_UNTIL < 64) { - if (simdjson_unlikely(STEP_UNTIL < cnt)) { - for (int i = STEP_UNTIL; i < cnt; i++) { - write_index(idx, bits, i); - } - } - } + write_indexes_stepped<0, STEP_UNTIL, STEP>(idx, bits, cnt); + SIMDJSON_IF_CONSTEXPR (STEP_UNTIL < 64) { + if (simdjson_unlikely(STEP_UNTIL < cnt)) { + for (int i=STEP_UNTIL; itail += cnt; - } + this->tail += cnt; + } #endif // SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER - }; +}; - class json_structural_indexer { - public: - /** - * Find the important bits of JSON in a 128-byte chunk, and add them to structural_indexes. - * - * @param partial Setting the partial parameter to true allows the find_structural_bits to - * tolerate unclosed strings. The caller should still ensure that the input is valid UTF-8. If - * you are processing substrings, you may want to call on a function like trimmed_length_safe_utf8. - */ - template - static error_code index(const uint8_t* buf, size_t len, dom_parser_implementation& parser, stage1_mode partial) noexcept; +class json_structural_indexer { +public: + /** + * Find the important bits of JSON in a 128-byte chunk, and add them to structural_indexes. + * + * @param partial Setting the partial parameter to true allows the find_structural_bits to + * tolerate unclosed strings. The caller should still ensure that the input is valid UTF-8. If + * you are processing substrings, you may want to call on a function like trimmed_length_safe_utf8. + */ + template + static error_code index(const uint8_t *buf, size_t len, dom_parser_implementation &parser, stage1_mode partial) noexcept; - private: - simdjson_inline json_structural_indexer(uint32_t* structural_indexes); - template - simdjson_inline void step(const uint8_t* block, buf_block_reader& reader) noexcept; - simdjson_inline void next(const simd::simd8x64& in, const json_block& block, size_t idx); - simdjson_inline error_code finish(dom_parser_implementation& parser, size_t idx, size_t len, stage1_mode partial); +private: + simdjson_inline json_structural_indexer(uint32_t *structural_indexes); + template + simdjson_inline void step(const uint8_t *block, buf_block_reader &reader) noexcept; + simdjson_inline void next(const simd::simd8x64& in, const json_block& block, size_t idx); + simdjson_warn_unused simdjson_inline error_code finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial); - json_scanner scanner{}; - utf8_checker checker{}; - bit_indexer indexer; - uint64_t prev_structurals = 0; - uint64_t unescaped_chars_error = 0; - }; + json_scanner scanner{}; + utf8_checker checker{}; + bit_indexer indexer; + uint64_t prev_structurals = 0; + uint64_t unescaped_chars_error = 0; +}; - simdjson_inline json_structural_indexer::json_structural_indexer(uint32_t* structural_indexes) : indexer{ structural_indexes } {} +simdjson_inline json_structural_indexer::json_structural_indexer(uint32_t *structural_indexes) : indexer{structural_indexes} {} - // Skip the last character if it is partial - simdjson_inline size_t trim_partial_utf8(const uint8_t* buf, size_t len) { - if (simdjson_unlikely(len < 3)) { - switch (len) { - case 2: - if (buf[len - 1] >= 0xc0) { return len - 1; } // 2-, 3- and 4-byte characters with only 1 byte left - if (buf[len - 2] >= 0xe0) { return len - 2; } // 3- and 4-byte characters with only 2 bytes left - return len; - case 1: - if (buf[len - 1] >= 0xc0) { return len - 1; } // 2-, 3- and 4-byte characters with only 1 byte left - return len; - case 0: - return len; - } - } - if (buf[len - 1] >= 0xc0) { return len - 1; } // 2-, 3- and 4-byte characters with only 1 byte left - if (buf[len - 2] >= 0xe0) { return len - 2; } // 3- and 4-byte characters with only 1 byte left - if (buf[len - 3] >= 0xf0) { return len - 3; } // 4-byte characters with only 3 bytes left - return len; - } +// Skip the last character if it is partial +simdjson_inline size_t trim_partial_utf8(const uint8_t *buf, size_t len) { + if (simdjson_unlikely(len < 3)) { + switch (len) { + case 2: + if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left + if (buf[len-2] >= 0xe0) { return len-2; } // 3- and 4-byte characters with only 2 bytes left + return len; + case 1: + if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left + return len; + case 0: + return len; + } + } + if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left + if (buf[len-2] >= 0xe0) { return len-2; } // 3- and 4-byte characters with only 1 byte left + if (buf[len-3] >= 0xf0) { return len-3; } // 4-byte characters with only 3 bytes left + return len; +} - // - // PERF NOTES: - // We pipe 2 inputs through these stages: - // 1. Load JSON into registers. This takes a long time and is highly parallelizable, so we load - // 2 inputs' worth at once so that by the time step 2 is looking for them input, it's available. - // 2. Scan the JSON for critical data: strings, scalars and operators. This is the critical path. - // The output of step 1 depends entirely on this information. These functions don't quite use - // up enough CPU: the second half of the functions is highly serial, only using 1 execution core - // at a time. The second input's scans has some dependency on the first ones finishing it, but - // they can make a lot of progress before they need that information. - // 3. Step 1 does not use enough capacity, so we run some extra stuff while we're waiting for that - // to finish: utf-8 checks and generating the output from the last iteration. - // - // The reason we run 2 inputs at a time, is steps 2 and 3 are *still* not enough to soak up all - // available capacity with just one input. Running 2 at a time seems to give the CPU a good enough - // workout. - // - template - error_code json_structural_indexer::index(const uint8_t* buf, size_t len, dom_parser_implementation& parser, stage1_mode partial) noexcept { - if (simdjson_unlikely(len > parser.capacity())) { return CAPACITY; } - // We guard the rest of the code so that we can assume that len > 0 throughout. - if (len == 0) { return EMPTY; } - if (is_streaming(partial)) { - len = trim_partial_utf8(buf, len); - // If you end up with an empty window after trimming - // the partial UTF-8 bytes, then chances are good that you - // have an UTF-8 formatting error. - if (len == 0) { return UTF8_ERROR; } - } - buf_block_reader reader(buf, len); - json_structural_indexer indexer(parser.structural_indexes.get()); +// +// PERF NOTES: +// We pipe 2 inputs through these stages: +// 1. Load JSON into registers. This takes a long time and is highly parallelizable, so we load +// 2 inputs' worth at once so that by the time step 2 is looking for them input, it's available. +// 2. Scan the JSON for critical data: strings, scalars and operators. This is the critical path. +// The output of step 1 depends entirely on this information. These functions don't quite use +// up enough CPU: the second half of the functions is highly serial, only using 1 execution core +// at a time. The second input's scans has some dependency on the first ones finishing it, but +// they can make a lot of progress before they need that information. +// 3. Step 1 does not use enough capacity, so we run some extra stuff while we're waiting for that +// to finish: utf-8 checks and generating the output from the last iteration. +// +// The reason we run 2 inputs at a time, is steps 2 and 3 are *still* not enough to soak up all +// available capacity with just one input. Running 2 at a time seems to give the CPU a good enough +// workout. +// +template +error_code json_structural_indexer::index(const uint8_t *buf, size_t len, dom_parser_implementation &parser, stage1_mode partial) noexcept { + if (simdjson_unlikely(len > parser.capacity())) { return CAPACITY; } + // We guard the rest of the code so that we can assume that len > 0 throughout. + if (len == 0) { return EMPTY; } + if (is_streaming(partial)) { + len = trim_partial_utf8(buf, len); + // If you end up with an empty window after trimming + // the partial UTF-8 bytes, then chances are good that you + // have an UTF-8 formatting error. + if(len == 0) { return UTF8_ERROR; } + } + buf_block_reader reader(buf, len); + json_structural_indexer indexer(parser.structural_indexes.get()); - // Read all but the last block - while (reader.has_full_block()) { - indexer.step(reader.full_block(), reader); - } - // Take care of the last block (will always be there unless file is empty which is - // not supposed to happen.) - uint8_t block[STEP_SIZE]; - if (simdjson_unlikely(reader.get_remainder(block) == 0)) { return UNEXPECTED_ERROR; } - indexer.step(block, reader); - return indexer.finish(parser, reader.block_index(), len, partial); - } + // Read all but the last block + while (reader.has_full_block()) { + indexer.step(reader.full_block(), reader); + } + // Take care of the last block (will always be there unless file is empty which is + // not supposed to happen.) + uint8_t block[STEP_SIZE]; + if (simdjson_unlikely(reader.get_remainder(block) == 0)) { return UNEXPECTED_ERROR; } + indexer.step(block, reader); + return indexer.finish(parser, reader.block_index(), len, partial); +} - template<> - simdjson_inline void json_structural_indexer::step<128>(const uint8_t* block, buf_block_reader<128>& reader) noexcept { - simd::simd8x64 in_1(block); - simd::simd8x64 in_2(block + 64); - json_block block_1 = scanner.next(in_1); - json_block block_2 = scanner.next(in_2); - this->next(in_1, block_1, reader.block_index()); - this->next(in_2, block_2, reader.block_index() + 64); - reader.advance(); - } +template<> +simdjson_inline void json_structural_indexer::step<128>(const uint8_t *block, buf_block_reader<128> &reader) noexcept { + simd::simd8x64 in_1(block); + simd::simd8x64 in_2(block+64); + json_block block_1 = scanner.next(in_1); + json_block block_2 = scanner.next(in_2); + this->next(in_1, block_1, reader.block_index()); + this->next(in_2, block_2, reader.block_index()+64); + reader.advance(); +} - template<> - simdjson_inline void json_structural_indexer::step<64>(const uint8_t* block, buf_block_reader<64>& reader) noexcept { - simd::simd8x64 in_1(block); - json_block block_1 = scanner.next(in_1); - this->next(in_1, block_1, reader.block_index()); - reader.advance(); - } +template<> +simdjson_inline void json_structural_indexer::step<64>(const uint8_t *block, buf_block_reader<64> &reader) noexcept { + simd::simd8x64 in_1(block); + json_block block_1 = scanner.next(in_1); + this->next(in_1, block_1, reader.block_index()); + reader.advance(); +} - simdjson_inline void json_structural_indexer::next(const simd::simd8x64& in, const json_block& block, size_t idx) { - uint64_t unescaped = in.lteq(0x1F); +simdjson_inline void json_structural_indexer::next(const simd::simd8x64& in, const json_block& block, size_t idx) { + uint64_t unescaped = in.lteq(0x1F); #if SIMDJSON_UTF8VALIDATION - checker.check_next_input(in); + checker.check_next_input(in); #endif - indexer.write(uint32_t(idx - 64), prev_structurals); // Output *last* iteration's structurals to the parser - prev_structurals = block.structural_start(); - unescaped_chars_error |= block.non_quote_inside_string(unescaped); - } + indexer.write(uint32_t(idx-64), prev_structurals); // Output *last* iteration's structurals to the parser + prev_structurals = block.structural_start(); + unescaped_chars_error |= block.non_quote_inside_string(unescaped); +} - simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementation& parser, size_t idx, size_t len, stage1_mode partial) { - // Write out the final iteration's structurals - indexer.write(uint32_t(idx - 64), prev_structurals); - error_code error = scanner.finish(); - // We deliberately break down the next expression so that it is - // human readable. - const bool should_we_exit = is_streaming(partial) ? - ((error != SUCCESS) && (error != UNCLOSED_STRING)) // when partial we tolerate UNCLOSED_STRING - : (error != SUCCESS); // if partial is false, we must have SUCCESS - const bool have_unclosed_string = (error == UNCLOSED_STRING); - if (simdjson_unlikely(should_we_exit)) { return error; } +simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial) { + // Write out the final iteration's structurals + indexer.write(uint32_t(idx-64), prev_structurals); + error_code error = scanner.finish(); + // We deliberately break down the next expression so that it is + // human readable. + const bool should_we_exit = is_streaming(partial) ? + ((error != SUCCESS) && (error != UNCLOSED_STRING)) // when partial we tolerate UNCLOSED_STRING + : (error != SUCCESS); // if partial is false, we must have SUCCESS + const bool have_unclosed_string = (error == UNCLOSED_STRING); + if (simdjson_unlikely(should_we_exit)) { return error; } - if (unescaped_chars_error) { - return UNESCAPED_CHARS; - } - parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get()); - /*** - * The On-Demand API requires special padding. - * - * This is related to https://github.com/simdjson/simdjson/issues/906 - * Basically, we want to make sure that if the parsing continues beyond the last (valid) - * structural character, it quickly stops. - * Only three structural characters can be repeated without triggering an error in JSON: [,] and }. - * We repeat the padding character (at 'len'). We don't know what it is, but if the parsing - * continues, then it must be [,] or }. - * Suppose it is ] or }. We backtrack to the first character, what could it be that would - * not trigger an error? It could be ] or } but no, because you can't start a document that way. - * It can't be a comma, a colon or any simple value. So the only way we could continue is - * if the repeated character is [. But if so, the document must start with [. But if the document - * starts with [, it should end with ]. If we enforce that rule, then we would get - * ][[ which is invalid. - * - * This is illustrated with the test array_iterate_unclosed_error() on the following input: - * R"({ "a": [,,)" - **/ - parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); // used later in partial == stage1_mode::streaming_final - parser.structural_indexes[parser.n_structural_indexes + 1] = uint32_t(len); - parser.structural_indexes[parser.n_structural_indexes + 2] = 0; - parser.next_structural_index = 0; - // a valid JSON file cannot have zero structural indexes - we should have found something - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { - return EMPTY; - } - if (simdjson_unlikely(parser.structural_indexes[parser.n_structural_indexes - 1] > len)) { - return UNEXPECTED_ERROR; - } - if (partial == stage1_mode::streaming_partial) { - // If we have an unclosed string, then the last structural - // will be the quote and we want to make sure to omit it. - if (have_unclosed_string) { - parser.n_structural_indexes--; - // a valid JSON file cannot have zero structural indexes - we should have found something - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; } - } - // We truncate the input to the end of the last complete document (or zero). - auto new_structural_indexes = find_next_document_index(parser); - if (new_structural_indexes == 0 && parser.n_structural_indexes > 0) { - if (parser.structural_indexes[0] == 0) { - // If the buffer is partial and we started at index 0 but the document is - // incomplete, it's too big to parse. - return CAPACITY; - } - else { - // It is possible that the document could be parsed, we just had a lot - // of white space. - parser.n_structural_indexes = 0; - return EMPTY; - } - } + if (unescaped_chars_error) { + return UNESCAPED_CHARS; + } + parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get()); + /*** + * The On-Demand API requires special padding. + * + * This is related to https://github.com/simdjson/simdjson/issues/906 + * Basically, we want to make sure that if the parsing continues beyond the last (valid) + * structural character, it quickly stops. + * Only three structural characters can be repeated without triggering an error in JSON: [,] and }. + * We repeat the padding character (at 'len'). We don't know what it is, but if the parsing + * continues, then it must be [,] or }. + * Suppose it is ] or }. We backtrack to the first character, what could it be that would + * not trigger an error? It could be ] or } but no, because you can't start a document that way. + * It can't be a comma, a colon or any simple value. So the only way we could continue is + * if the repeated character is [. But if so, the document must start with [. But if the document + * starts with [, it should end with ]. If we enforce that rule, then we would get + * ][[ which is invalid. + * + * This is illustrated with the test array_iterate_unclosed_error() on the following input: + * R"({ "a": [,,)" + **/ + parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); // used later in partial == stage1_mode::streaming_final + parser.structural_indexes[parser.n_structural_indexes + 1] = uint32_t(len); + parser.structural_indexes[parser.n_structural_indexes + 2] = 0; + parser.next_structural_index = 0; + // a valid JSON file cannot have zero structural indexes - we should have found something + if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { + return EMPTY; + } + if (simdjson_unlikely(parser.structural_indexes[parser.n_structural_indexes - 1] > len)) { + return UNEXPECTED_ERROR; + } + if (partial == stage1_mode::streaming_partial) { + // If we have an unclosed string, then the last structural + // will be the quote and we want to make sure to omit it. + if(have_unclosed_string) { + parser.n_structural_indexes--; + // a valid JSON file cannot have zero structural indexes - we should have found something + if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; } + } + // We truncate the input to the end of the last complete document (or zero). + auto new_structural_indexes = find_next_document_index(parser); + if (new_structural_indexes == 0 && parser.n_structural_indexes > 0) { + if(parser.structural_indexes[0] == 0) { + // If the buffer is partial and we started at index 0 but the document is + // incomplete, it's too big to parse. + return CAPACITY; + } else { + // It is possible that the document could be parsed, we just had a lot + // of white space. + parser.n_structural_indexes = 0; + return EMPTY; + } + } - parser.n_structural_indexes = new_structural_indexes; - } - else if (partial == stage1_mode::streaming_final) { - if (have_unclosed_string) { parser.n_structural_indexes--; } - // We truncate the input to the end of the last complete document (or zero). - // Because partial == stage1_mode::streaming_final, it means that we may - // silently ignore trailing garbage. Though it sounds bad, we do it - // deliberately because many people who have streams of JSON documents - // will truncate them for processing. E.g., imagine that you are uncompressing - // the data from a size file or receiving it in chunks from the network. You - // may not know where exactly the last document will be. Meanwhile the - // document_stream instances allow people to know the JSON documents they are - // parsing (see the iterator.source() method). - parser.n_structural_indexes = find_next_document_index(parser); - // We store the initial n_structural_indexes so that the client can see - // whether we used truncation. If initial_n_structural_indexes == parser.n_structural_indexes, - // then this will query parser.structural_indexes[parser.n_structural_indexes] which is len, - // otherwise, it will copy some prior index. - parser.structural_indexes[parser.n_structural_indexes + 1] = parser.structural_indexes[parser.n_structural_indexes]; - // This next line is critical, do not change it unless you understand what you are - // doing. - parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { - // We tolerate an unclosed string at the very end of the stream. Indeed, users - // often load their data in bulk without being careful and they want us to ignore - // the trailing garbage. - return EMPTY; - } - } - checker.check_eof(); - return checker.errors(); - } + parser.n_structural_indexes = new_structural_indexes; + } else if (partial == stage1_mode::streaming_final) { + if(have_unclosed_string) { parser.n_structural_indexes--; } + // We truncate the input to the end of the last complete document (or zero). + // Because partial == stage1_mode::streaming_final, it means that we may + // silently ignore trailing garbage. Though it sounds bad, we do it + // deliberately because many people who have streams of JSON documents + // will truncate them for processing. E.g., imagine that you are uncompressing + // the data from a size file or receiving it in chunks from the network. You + // may not know where exactly the last document will be. Meanwhile the + // document_stream instances allow people to know the JSON documents they are + // parsing (see the iterator.source() method). + parser.n_structural_indexes = find_next_document_index(parser); + // We store the initial n_structural_indexes so that the client can see + // whether we used truncation. If initial_n_structural_indexes == parser.n_structural_indexes, + // then this will query parser.structural_indexes[parser.n_structural_indexes] which is len, + // otherwise, it will copy some prior index. + parser.structural_indexes[parser.n_structural_indexes + 1] = parser.structural_indexes[parser.n_structural_indexes]; + // This next line is critical, do not change it unless you understand what you are + // doing. + parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); + if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { + // We tolerate an unclosed string at the very end of the stream. Indeed, users + // often load their data in bulk without being careful and they want us to ignore + // the trailing garbage. + return EMPTY; + } + } + checker.check_eof(); + return checker.errors(); +} - } // namespace stage1 - } // unnamed namespace - } // namespace westmere +} // namespace stage1 +} // unnamed namespace +} // namespace westmere } // namespace simdjson // Clear CUSTOM_BIT_INDEXER so other implementations can set it if they need to. @@ -38818,38 +40597,38 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { - namespace stage1 { +namespace westmere { +namespace { +namespace stage1 { - /** - * Validates that the string is actual UTF-8. - */ - template - bool generic_validate_utf8(const uint8_t* input, size_t length) { - checker c{}; - buf_block_reader<64> reader(input, length); - while (reader.has_full_block()) { - simd::simd8x64 in(reader.full_block()); - c.check_next_input(in); - reader.advance(); - } - uint8_t block[64]{}; - reader.get_remainder(block); - simd::simd8x64 in(block); - c.check_next_input(in); - reader.advance(); - c.check_eof(); - return c.errors() == error_code::SUCCESS; - } +/** + * Validates that the string is actual UTF-8. + */ +template +bool generic_validate_utf8(const uint8_t * input, size_t length) { + checker c{}; + buf_block_reader<64> reader(input, length); + while (reader.has_full_block()) { + simd::simd8x64 in(reader.full_block()); + c.check_next_input(in); + reader.advance(); + } + uint8_t block[64]{}; + reader.get_remainder(block); + simd::simd8x64 in(block); + c.check_next_input(in); + reader.advance(); + c.check_eof(); + return c.errors() == error_code::SUCCESS; +} - bool generic_validate_utf8(const char* input, size_t length) { - return generic_validate_utf8(reinterpret_cast(input), length); - } +bool generic_validate_utf8(const char * input, size_t length) { + return generic_validate_utf8(reinterpret_cast(input),length); +} - } // namespace stage1 - } // unnamed namespace - } // namespace westmere +} // namespace stage1 +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_UTF8_VALIDATOR_H @@ -38868,18 +40647,18 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { - namespace stage2 { +namespace westmere { +namespace { +namespace stage2 { - class json_iterator; - class structural_iterator; - struct tape_builder; - struct tape_writer; +class json_iterator; +class structural_iterator; +struct tape_builder; +struct tape_writer; - } // namespace stage2 - } // unnamed namespace - } // namespace westmere +} // namespace stage2 +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_BASE_H @@ -38897,109 +40676,109 @@ namespace simdjson { #include namespace simdjson { - namespace westmere { - namespace { - namespace stage2 { +namespace westmere { +namespace { +namespace stage2 { - struct tape_writer { - /** The next place to write to tape */ - uint64_t* next_tape_loc; +struct tape_writer { + /** The next place to write to tape */ + uint64_t *next_tape_loc; - /** Write a signed 64-bit value to tape. */ - simdjson_inline void append_s64(int64_t value) noexcept; + /** Write a signed 64-bit value to tape. */ + simdjson_inline void append_s64(int64_t value) noexcept; - /** Write an unsigned 64-bit value to tape. */ - simdjson_inline void append_u64(uint64_t value) noexcept; + /** Write an unsigned 64-bit value to tape. */ + simdjson_inline void append_u64(uint64_t value) noexcept; - /** Write a double value to tape. */ - simdjson_inline void append_double(double value) noexcept; + /** Write a double value to tape. */ + simdjson_inline void append_double(double value) noexcept; - /** - * Append a tape entry (an 8-bit type,and 56 bits worth of value). - */ - simdjson_inline void append(uint64_t val, internal::tape_type t) noexcept; + /** + * Append a tape entry (an 8-bit type,and 56 bits worth of value). + */ + simdjson_inline void append(uint64_t val, internal::tape_type t) noexcept; - /** - * Skip the current tape entry without writing. - * - * Used to skip the start of the container, since we'll come back later to fill it in when the - * container ends. - */ - simdjson_inline void skip() noexcept; + /** + * Skip the current tape entry without writing. + * + * Used to skip the start of the container, since we'll come back later to fill it in when the + * container ends. + */ + simdjson_inline void skip() noexcept; - /** - * Skip the number of tape entries necessary to write a large u64 or i64. - */ - simdjson_inline void skip_large_integer() noexcept; + /** + * Skip the number of tape entries necessary to write a large u64 or i64. + */ + simdjson_inline void skip_large_integer() noexcept; - /** - * Skip the number of tape entries necessary to write a double. - */ - simdjson_inline void skip_double() noexcept; + /** + * Skip the number of tape entries necessary to write a double. + */ + simdjson_inline void skip_double() noexcept; - /** - * Write a value to a known location on tape. - * - * Used to go back and write out the start of a container after the container ends. - */ - simdjson_inline static void write(uint64_t& tape_loc, uint64_t val, internal::tape_type t) noexcept; + /** + * Write a value to a known location on tape. + * + * Used to go back and write out the start of a container after the container ends. + */ + simdjson_inline static void write(uint64_t &tape_loc, uint64_t val, internal::tape_type t) noexcept; - private: - /** - * Append both the tape entry, and a supplementary value following it. Used for types that need - * all 64 bits, such as double and uint64_t. - */ - template - simdjson_inline void append2(uint64_t val, T val2, internal::tape_type t) noexcept; - }; // struct tape_writer +private: + /** + * Append both the tape entry, and a supplementary value following it. Used for types that need + * all 64 bits, such as double and uint64_t. + */ + template + simdjson_inline void append2(uint64_t val, T val2, internal::tape_type t) noexcept; +}; // struct tape_writer - simdjson_inline void tape_writer::append_s64(int64_t value) noexcept { - append2(0, value, internal::tape_type::INT64); - } +simdjson_inline void tape_writer::append_s64(int64_t value) noexcept { + append2(0, value, internal::tape_type::INT64); +} - simdjson_inline void tape_writer::append_u64(uint64_t value) noexcept { - append(0, internal::tape_type::UINT64); - *next_tape_loc = value; - next_tape_loc++; - } +simdjson_inline void tape_writer::append_u64(uint64_t value) noexcept { + append(0, internal::tape_type::UINT64); + *next_tape_loc = value; + next_tape_loc++; +} - /** Write a double value to tape. */ - simdjson_inline void tape_writer::append_double(double value) noexcept { - append2(0, value, internal::tape_type::DOUBLE); - } +/** Write a double value to tape. */ +simdjson_inline void tape_writer::append_double(double value) noexcept { + append2(0, value, internal::tape_type::DOUBLE); +} - simdjson_inline void tape_writer::skip() noexcept { - next_tape_loc++; - } +simdjson_inline void tape_writer::skip() noexcept { + next_tape_loc++; +} - simdjson_inline void tape_writer::skip_large_integer() noexcept { - next_tape_loc += 2; - } +simdjson_inline void tape_writer::skip_large_integer() noexcept { + next_tape_loc += 2; +} - simdjson_inline void tape_writer::skip_double() noexcept { - next_tape_loc += 2; - } +simdjson_inline void tape_writer::skip_double() noexcept { + next_tape_loc += 2; +} - simdjson_inline void tape_writer::append(uint64_t val, internal::tape_type t) noexcept { - *next_tape_loc = val | ((uint64_t(char(t))) << 56); - next_tape_loc++; - } +simdjson_inline void tape_writer::append(uint64_t val, internal::tape_type t) noexcept { + *next_tape_loc = val | ((uint64_t(char(t))) << 56); + next_tape_loc++; +} - template - simdjson_inline void tape_writer::append2(uint64_t val, T val2, internal::tape_type t) noexcept { - append(val, t); - static_assert(sizeof(val2) == sizeof(*next_tape_loc), "Type is not 64 bits!"); - memcpy(next_tape_loc, &val2, sizeof(val2)); - next_tape_loc++; - } +template +simdjson_inline void tape_writer::append2(uint64_t val, T val2, internal::tape_type t) noexcept { + append(val, t); + static_assert(sizeof(val2) == sizeof(*next_tape_loc), "Type is not 64 bits!"); + memcpy(next_tape_loc, &val2, sizeof(val2)); + next_tape_loc++; +} - simdjson_inline void tape_writer::write(uint64_t& tape_loc, uint64_t val, internal::tape_type t) noexcept { - tape_loc = val | ((uint64_t(char(t))) << 56); - } +simdjson_inline void tape_writer::write(uint64_t &tape_loc, uint64_t val, internal::tape_type t) noexcept { + tape_loc = val | ((uint64_t(char(t))) << 56); +} - } // namespace stage2 - } // unnamed namespace - } // namespace westmere +} // namespace stage2 +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_WRITER_H @@ -39019,92 +40798,90 @@ namespace simdjson { // This is for an internal-only stage 2 specific logger. // Set LOG_ENABLED = true to log what stage 2 is doing! namespace simdjson { - namespace westmere { - namespace { - namespace logger { +namespace westmere { +namespace { +namespace logger { - static constexpr const char* DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"; + static constexpr const char * DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"; #if SIMDJSON_VERBOSE_LOGGING - static constexpr const bool LOG_ENABLED = true; + static constexpr const bool LOG_ENABLED = true; #else - static constexpr const bool LOG_ENABLED = false; + static constexpr const bool LOG_ENABLED = false; #endif - 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 constexpr const int LOG_INDEX_LEN = 5; + 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 constexpr const int LOG_INDEX_LEN = 5; - static int log_depth; // Not threadsafe. Log only. + static int log_depth; // Not threadsafe. Log only. - // Helper to turn unprintable or newline characters into spaces - static simdjson_inline char printable_char(char c) { - if (c >= 0x20) { - return c; - } - else { - return ' '; - } - } + // Helper to turn unprintable or newline characters into spaces + static simdjson_inline char printable_char(char c) { + if (c >= 0x20) { + return c; + } else { + return ' '; + } + } - // Print the header and set up log_start - static simdjson_inline void log_start() { - if (LOG_ENABLED) { - log_depth = 0; - printf("\n"); - printf("| %-*s | %-*s | %-*s | %-*s | Detail |\n", LOG_EVENT_LEN, "Event", LOG_BUFFER_LEN, "Buffer", LOG_SMALL_BUFFER_LEN, "Next", 5, "Next#"); - printf("|%.*s|%.*s|%.*s|%.*s|--------|\n", LOG_EVENT_LEN + 2, DASHES, LOG_BUFFER_LEN + 2, DASHES, LOG_SMALL_BUFFER_LEN + 2, DASHES, 5 + 2, DASHES); - } - } + // Print the header and set up log_start + static simdjson_inline void log_start() { + if (LOG_ENABLED) { + log_depth = 0; + printf("\n"); + printf("| %-*s | %-*s | %-*s | %-*s | Detail |\n", LOG_EVENT_LEN, "Event", LOG_BUFFER_LEN, "Buffer", LOG_SMALL_BUFFER_LEN, "Next", 5, "Next#"); + printf("|%.*s|%.*s|%.*s|%.*s|--------|\n", LOG_EVENT_LEN+2, DASHES, LOG_BUFFER_LEN+2, DASHES, LOG_SMALL_BUFFER_LEN+2, DASHES, 5+2, DASHES); + } + } - simdjson_unused static simdjson_inline void log_string(const char* message) { - if (LOG_ENABLED) { - printf("%s\n", message); - } - } + simdjson_unused static simdjson_inline void log_string(const char *message) { + if (LOG_ENABLED) { + printf("%s\n", message); + } + } - // Logs a single line from the stage 2 DOM parser - template - static simdjson_inline void log_line(S& structurals, const char* title_prefix, const char* title, const char* detail) { - if (LOG_ENABLED) { - printf("| %*s%s%-*s ", log_depth * 2, "", title_prefix, LOG_EVENT_LEN - log_depth * 2 - int(strlen(title_prefix)), title); - auto current_index = structurals.at_beginning() ? nullptr : structurals.next_structural - 1; - auto next_index = structurals.next_structural; - auto current = current_index ? &structurals.buf[*current_index] : reinterpret_cast(" "); - auto next = &structurals.buf[*next_index]; - { - // Print the next N characters in the buffer. - printf("| "); - // Otherwise, print the characters starting from the buffer position. - // Print spaces for unprintable or newline characters. - for (int i = 0; i < LOG_BUFFER_LEN; i++) { - printf("%c", printable_char(current[i])); - } - printf(" "); - // Print the next N characters in the buffer. - printf("| "); - // Otherwise, print the characters starting from the buffer position. - // Print spaces for unprintable or newline characters. - for (int i = 0; i < LOG_SMALL_BUFFER_LEN; i++) { - printf("%c", printable_char(next[i])); - } - printf(" "); - } - if (current_index) { - printf("| %*u ", LOG_INDEX_LEN, *current_index); - } - else { - printf("| %-*s ", LOG_INDEX_LEN, ""); - } - // printf("| %*u ", LOG_INDEX_LEN, structurals.next_tape_index()); - printf("| %-s ", detail); - printf("|\n"); - } - } + // Logs a single line from the stage 2 DOM parser + template + static simdjson_inline void log_line(S &structurals, const char *title_prefix, const char *title, const char *detail) { + if (LOG_ENABLED) { + printf("| %*s%s%-*s ", log_depth*2, "", title_prefix, LOG_EVENT_LEN - log_depth*2 - int(strlen(title_prefix)), title); + auto current_index = structurals.at_beginning() ? nullptr : structurals.next_structural-1; + auto next_index = structurals.next_structural; + auto current = current_index ? &structurals.buf[*current_index] : reinterpret_cast(" "); + auto next = &structurals.buf[*next_index]; + { + // Print the next N characters in the buffer. + printf("| "); + // Otherwise, print the characters starting from the buffer position. + // Print spaces for unprintable or newline characters. + for (int i=0;i - simdjson_warn_unused simdjson_inline error_code walk_document(V& visitor) noexcept; + /** + * Walk the JSON document. + * + * The visitor receives callbacks when values are encountered. All callbacks pass the iterator as + * the first parameter; some callbacks have other parameters as well: + * + * - visit_document_start() - at the beginning. + * - visit_document_end() - at the end (if things were successful). + * + * - visit_array_start() - at the start `[` of a non-empty array. + * - visit_array_end() - at the end `]` of a non-empty array. + * - visit_empty_array() - when an empty array is encountered. + * + * - visit_object_end() - at the start `]` of a non-empty object. + * - visit_object_start() - at the end `]` of a non-empty object. + * - visit_empty_object() - when an empty object is encountered. + * - visit_key(const uint8_t *key) - when a key in an object field is encountered. key is + * guaranteed to point at the first quote of the string (`"key"`). + * - visit_primitive(const uint8_t *value) - when a value is a string, number, boolean or null. + * - visit_root_primitive(iter, uint8_t *value) - when the top-level value is a string, number, boolean or null. + * + * - increment_count(iter) - each time a value is found in an array or object. + */ + template + simdjson_warn_unused simdjson_inline error_code walk_document(V &visitor) noexcept; - /** - * Create an iterator capable of walking a JSON document. - * - * The document must have already passed through stage 1. - */ - simdjson_inline json_iterator(dom_parser_implementation& _dom_parser, size_t start_structural_index); + /** + * Create an iterator capable of walking a JSON document. + * + * The document must have already passed through stage 1. + */ + simdjson_inline json_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index); - /** - * Look at the next token. - * - * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). - * - * They may include invalid JSON as well (such as `1.2.3` or `ture`). - */ - simdjson_inline const uint8_t* peek() const noexcept; - /** - * Advance to the next token. - * - * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). - * - * They may include invalid JSON as well (such as `1.2.3` or `ture`). - */ - simdjson_inline const uint8_t* advance() noexcept; - /** - * Get the remaining length of the document, from the start of the current token. - */ - simdjson_inline size_t remaining_len() const noexcept; - /** - * Check if we are at the end of the document. - * - * If this is true, there are no more tokens. - */ - simdjson_inline bool at_eof() const noexcept; - /** - * Check if we are at the beginning of the document. - */ - simdjson_inline bool at_beginning() const noexcept; - simdjson_inline uint8_t last_structural() const noexcept; + /** + * Look at the next token. + * + * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). + * + * They may include invalid JSON as well (such as `1.2.3` or `ture`). + */ + simdjson_inline const uint8_t *peek() const noexcept; + /** + * Advance to the next token. + * + * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). + * + * They may include invalid JSON as well (such as `1.2.3` or `ture`). + */ + simdjson_inline const uint8_t *advance() noexcept; + /** + * Get the remaining length of the document, from the start of the current token. + */ + simdjson_inline size_t remaining_len() const noexcept; + /** + * Check if we are at the end of the document. + * + * If this is true, there are no more tokens. + */ + simdjson_inline bool at_eof() const noexcept; + /** + * Check if we are at the beginning of the document. + */ + simdjson_inline bool at_beginning() const noexcept; + simdjson_inline uint8_t last_structural() const noexcept; - /** - * Log that a value has been found. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_value(const char* type) const noexcept; - /** - * Log the start of a multipart value. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_start_value(const char* type) const noexcept; - /** - * Log the end of a multipart value. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_end_value(const char* type) const noexcept; - /** - * Log an error. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_error(const char* error) const noexcept; + /** + * Log that a value has been found. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_value(const char *type) const noexcept; + /** + * Log the start of a multipart value. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_start_value(const char *type) const noexcept; + /** + * Log the end of a multipart value. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_end_value(const char *type) const noexcept; + /** + * Log an error. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_error(const char *error) const noexcept; - template - simdjson_warn_unused simdjson_inline error_code visit_root_primitive(V& visitor, const uint8_t* value) noexcept; - template - simdjson_warn_unused simdjson_inline error_code visit_primitive(V& visitor, const uint8_t* value) noexcept; - }; + template + simdjson_warn_unused simdjson_inline error_code visit_root_primitive(V &visitor, const uint8_t *value) noexcept; + template + simdjson_warn_unused simdjson_inline error_code visit_primitive(V &visitor, const uint8_t *value) noexcept; +}; - template - simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V& visitor) noexcept { - logger::log_start(); +template +simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &visitor) noexcept { + logger::log_start(); - // - // Start the document - // - if (at_eof()) { return EMPTY; } - log_start_value("document"); - SIMDJSON_TRY(visitor.visit_document_start(*this)); + // + // Start the document + // + if (at_eof()) { return EMPTY; } + log_start_value("document"); + SIMDJSON_TRY( visitor.visit_document_start(*this) ); - // - // Read first value - // - { - auto value = advance(); + // + // Read first value + // + { + auto value = advance(); - // Make sure the outer object or array is closed before continuing; otherwise, there are ways we - // could get into memory corruption. See https://github.com/simdjson/simdjson/issues/906 - if (!STREAMING) { - switch (*value) { - case '{': if (last_structural() != '}') { log_value("starting brace unmatched"); return TAPE_ERROR; }; break; - case '[': if (last_structural() != ']') { log_value("starting bracket unmatched"); return TAPE_ERROR; }; break; - } - } + // Make sure the outer object or array is closed before continuing; otherwise, there are ways we + // could get into memory corruption. See https://github.com/simdjson/simdjson/issues/906 + if (!STREAMING) { + switch (*value) { + case '{': if (last_structural() != '}') { log_value("starting brace unmatched"); return TAPE_ERROR; }; break; + case '[': if (last_structural() != ']') { log_value("starting bracket unmatched"); return TAPE_ERROR; }; break; + } + } - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY(visitor.visit_empty_object(*this)); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY(visitor.visit_empty_array(*this)); break; } goto array_begin; - default: SIMDJSON_TRY(visitor.visit_root_primitive(*this, value)); break; - } - } - goto document_end; + switch (*value) { + case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; + case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; + default: SIMDJSON_TRY( visitor.visit_root_primitive(*this, value) ); break; + } + } + goto document_end; - // - // Object parser states - // - object_begin: - log_start_value("object"); - depth++; - if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } - dom_parser.is_array[depth] = false; - SIMDJSON_TRY(visitor.visit_object_start(*this)); +// +// Object parser states +// +object_begin: + log_start_value("object"); + depth++; + if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } + dom_parser.is_array[depth] = false; + SIMDJSON_TRY( visitor.visit_object_start(*this) ); - { - auto key = advance(); - if (*key != '"') { log_error("Object does not start with a key"); return TAPE_ERROR; } - SIMDJSON_TRY(visitor.increment_count(*this)); - SIMDJSON_TRY(visitor.visit_key(*this, key)); - } + { + auto key = advance(); + if (*key != '"') { log_error("Object does not start with a key"); return TAPE_ERROR; } + SIMDJSON_TRY( visitor.increment_count(*this) ); + SIMDJSON_TRY( visitor.visit_key(*this, key) ); + } - object_field: - if (simdjson_unlikely(*advance() != ':')) { log_error("Missing colon after key in object"); return TAPE_ERROR; } - { - auto value = advance(); - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY(visitor.visit_empty_object(*this)); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY(visitor.visit_empty_array(*this)); break; } goto array_begin; - default: SIMDJSON_TRY(visitor.visit_primitive(*this, value)); break; - } - } +object_field: + if (simdjson_unlikely( *advance() != ':' )) { log_error("Missing colon after key in object"); return TAPE_ERROR; } + { + auto value = advance(); + switch (*value) { + case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; + case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; + default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break; + } + } - object_continue: - switch (*advance()) { - case ',': - SIMDJSON_TRY(visitor.increment_count(*this)); - { - auto key = advance(); - if (simdjson_unlikely(*key != '"')) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; } - SIMDJSON_TRY(visitor.visit_key(*this, key)); - } - goto object_field; - case '}': log_end_value("object"); SIMDJSON_TRY(visitor.visit_object_end(*this)); goto scope_end; - default: log_error("No comma between object fields"); return TAPE_ERROR; - } +object_continue: + switch (*advance()) { + case ',': + SIMDJSON_TRY( visitor.increment_count(*this) ); + { + auto key = advance(); + if (simdjson_unlikely( *key != '"' )) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; } + SIMDJSON_TRY( visitor.visit_key(*this, key) ); + } + goto object_field; + case '}': log_end_value("object"); SIMDJSON_TRY( visitor.visit_object_end(*this) ); goto scope_end; + default: log_error("No comma between object fields"); return TAPE_ERROR; + } - scope_end: - depth--; - if (depth == 0) { goto document_end; } - if (dom_parser.is_array[depth]) { goto array_continue; } - goto object_continue; +scope_end: + depth--; + if (depth == 0) { goto document_end; } + if (dom_parser.is_array[depth]) { goto array_continue; } + goto object_continue; - // - // Array parser states - // - array_begin: - log_start_value("array"); - depth++; - if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } - dom_parser.is_array[depth] = true; - SIMDJSON_TRY(visitor.visit_array_start(*this)); - SIMDJSON_TRY(visitor.increment_count(*this)); +// +// Array parser states +// +array_begin: + log_start_value("array"); + depth++; + if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } + dom_parser.is_array[depth] = true; + SIMDJSON_TRY( visitor.visit_array_start(*this) ); + SIMDJSON_TRY( visitor.increment_count(*this) ); - array_value: - { - auto value = advance(); - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY(visitor.visit_empty_object(*this)); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY(visitor.visit_empty_array(*this)); break; } goto array_begin; - default: SIMDJSON_TRY(visitor.visit_primitive(*this, value)); break; - } - } +array_value: + { + auto value = advance(); + switch (*value) { + case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; + case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; + default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break; + } + } - array_continue: - switch (*advance()) { - case ',': SIMDJSON_TRY(visitor.increment_count(*this)); goto array_value; - case ']': log_end_value("array"); SIMDJSON_TRY(visitor.visit_array_end(*this)); goto scope_end; - default: log_error("Missing comma between array values"); return TAPE_ERROR; - } +array_continue: + switch (*advance()) { + case ',': SIMDJSON_TRY( visitor.increment_count(*this) ); goto array_value; + case ']': log_end_value("array"); SIMDJSON_TRY( visitor.visit_array_end(*this) ); goto scope_end; + default: log_error("Missing comma between array values"); return TAPE_ERROR; + } - document_end: - log_end_value("document"); - SIMDJSON_TRY(visitor.visit_document_end(*this)); +document_end: + log_end_value("document"); + SIMDJSON_TRY( visitor.visit_document_end(*this) ); - dom_parser.next_structural_index = uint32_t(next_structural - &dom_parser.structural_indexes[0]); + dom_parser.next_structural_index = uint32_t(next_structural - &dom_parser.structural_indexes[0]); - // If we didn't make it to the end, it's an error - if (!STREAMING && dom_parser.next_structural_index != dom_parser.n_structural_indexes) { - log_error("More than one JSON value at the root of the document, or extra characters at the end of the JSON!"); - return TAPE_ERROR; - } + // If we didn't make it to the end, it's an error + if ( !STREAMING && dom_parser.next_structural_index != dom_parser.n_structural_indexes ) { + log_error("More than one JSON value at the root of the document, or extra characters at the end of the JSON!"); + return TAPE_ERROR; + } - return SUCCESS; + return SUCCESS; - } // walk_document() +} // walk_document() - simdjson_inline json_iterator::json_iterator(dom_parser_implementation& _dom_parser, size_t start_structural_index) - : buf{ _dom_parser.buf }, - next_structural{ &_dom_parser.structural_indexes[start_structural_index] }, - dom_parser{ _dom_parser } { - } +simdjson_inline json_iterator::json_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index) + : buf{_dom_parser.buf}, + next_structural{&_dom_parser.structural_indexes[start_structural_index]}, + dom_parser{_dom_parser} { +} - simdjson_inline const uint8_t* json_iterator::peek() const noexcept { - return &buf[*(next_structural)]; - } - simdjson_inline const uint8_t* json_iterator::advance() noexcept { - return &buf[*(next_structural++)]; - } - simdjson_inline size_t json_iterator::remaining_len() const noexcept { - return dom_parser.len - *(next_structural - 1); - } +simdjson_inline const uint8_t *json_iterator::peek() const noexcept { + return &buf[*(next_structural)]; +} +simdjson_inline const uint8_t *json_iterator::advance() noexcept { + return &buf[*(next_structural++)]; +} +simdjson_inline size_t json_iterator::remaining_len() const noexcept { + return dom_parser.len - *(next_structural-1); +} - simdjson_inline bool json_iterator::at_eof() const noexcept { - return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; - } - simdjson_inline bool json_iterator::at_beginning() const noexcept { - return next_structural == dom_parser.structural_indexes.get(); - } - simdjson_inline uint8_t json_iterator::last_structural() const noexcept { - return buf[dom_parser.structural_indexes[dom_parser.n_structural_indexes - 1]]; - } +simdjson_inline bool json_iterator::at_eof() const noexcept { + return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; +} +simdjson_inline bool json_iterator::at_beginning() const noexcept { + return next_structural == dom_parser.structural_indexes.get(); +} +simdjson_inline uint8_t json_iterator::last_structural() const noexcept { + return buf[dom_parser.structural_indexes[dom_parser.n_structural_indexes - 1]]; +} - simdjson_inline void json_iterator::log_value(const char* type) const noexcept { - logger::log_line(*this, "", type, ""); - } +simdjson_inline void json_iterator::log_value(const char *type) const noexcept { + logger::log_line(*this, "", type, ""); +} - simdjson_inline void json_iterator::log_start_value(const char* type) const noexcept { - logger::log_line(*this, "+", type, ""); - if (logger::LOG_ENABLED) { logger::log_depth++; } - } +simdjson_inline void json_iterator::log_start_value(const char *type) const noexcept { + logger::log_line(*this, "+", type, ""); + if (logger::LOG_ENABLED) { logger::log_depth++; } +} - simdjson_inline void json_iterator::log_end_value(const char* type) const noexcept { - if (logger::LOG_ENABLED) { logger::log_depth--; } - logger::log_line(*this, "-", type, ""); - } +simdjson_inline void json_iterator::log_end_value(const char *type) const noexcept { + if (logger::LOG_ENABLED) { logger::log_depth--; } + logger::log_line(*this, "-", type, ""); +} - simdjson_inline void json_iterator::log_error(const char* error) const noexcept { - logger::log_line(*this, "", "ERROR", error); - } +simdjson_inline void json_iterator::log_error(const char *error) const noexcept { + logger::log_line(*this, "", "ERROR", error); +} - template - simdjson_warn_unused simdjson_inline error_code json_iterator::visit_root_primitive(V& visitor, const uint8_t* value) noexcept { - switch (*value) { - case '"': return visitor.visit_root_string(*this, value); - case 't': return visitor.visit_root_true_atom(*this, value); - case 'f': return visitor.visit_root_false_atom(*this, value); - case 'n': return visitor.visit_root_null_atom(*this, value); - case '-': - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - return visitor.visit_root_number(*this, value); - default: - log_error("Document starts with a non-value character"); - return TAPE_ERROR; - } - } - template - simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V& visitor, const uint8_t* value) noexcept { - // Use the fact that most scalars are going to be either strings or numbers. - if (*value == '"') { - return visitor.visit_string(*this, value); - } - else if (((*value - '0') < 10) || (*value == '-')) { - return visitor.visit_number(*this, value); - } - // true, false, null are uncommon. - switch (*value) { - case 't': return visitor.visit_true_atom(*this, value); - case 'f': return visitor.visit_false_atom(*this, value); - case 'n': return visitor.visit_null_atom(*this, value); - default: - log_error("Non-value found when value was expected!"); - return TAPE_ERROR; - } - } +template +simdjson_warn_unused simdjson_inline error_code json_iterator::visit_root_primitive(V &visitor, const uint8_t *value) noexcept { + switch (*value) { + case '"': return visitor.visit_root_string(*this, value); + case 't': return visitor.visit_root_true_atom(*this, value); + case 'f': return visitor.visit_root_false_atom(*this, value); + case 'n': return visitor.visit_root_null_atom(*this, value); + case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return visitor.visit_root_number(*this, value); + default: + log_error("Document starts with a non-value character"); + return TAPE_ERROR; + } +} +template +simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V &visitor, const uint8_t *value) noexcept { + // Use the fact that most scalars are going to be either strings or numbers. + if(*value == '"') { + return visitor.visit_string(*this, value); + } else if (((*value - '0') < 10) || (*value == '-')) { + return visitor.visit_number(*this, value); + } + // true, false, null are uncommon. + switch (*value) { + case 't': return visitor.visit_true_atom(*this, value); + case 'f': return visitor.visit_false_atom(*this, value); + case 'n': return visitor.visit_null_atom(*this, value); + default: + log_error("Non-value found when value was expected!"); + return TAPE_ERROR; + } +} - } // namespace stage2 - } // unnamed namespace - } // namespace westmere +} // namespace stage2 +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_JSON_ITERATOR_H /* end file generic/stage2/json_iterator.h for westmere */ /* including generic/stage2/stringparsing.h for westmere: #include */ /* begin file generic/stage2/stringparsing.h for westmere */ +#include #ifndef SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -39457,242 +41234,238 @@ namespace simdjson { // It is intended to be included multiple times and compiled multiple times namespace simdjson { - namespace westmere { - namespace { - /// @private - namespace stringparsing { +namespace westmere { +namespace { +/// @private +namespace stringparsing { - // begin copypasta - // These chars yield themselves: " \ / - // b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab - // u not handled in this table as it's complex - static const uint8_t escape_map[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// begin copypasta +// These chars yield themselves: " \ / +// b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab +// u not handled in this table as it's complex +static const uint8_t escape_map[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0. + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5. - 0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6. - 0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7. + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4. + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5. + 0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6. + 0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - }; + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; - // handle a unicode codepoint - // write appropriate values into dest - // src will advance 6 bytes or 12 bytes - // dest will advance a variable amount (return via pointer) - // return true if the unicode codepoint was valid - // We work in little-endian then swap at write time - simdjson_warn_unused - simdjson_inline bool handle_unicode_codepoint(const uint8_t** src_ptr, - uint8_t** dst_ptr, bool allow_replacement) { - // Use the default Unicode Character 'REPLACEMENT CHARACTER' (U+FFFD) - constexpr uint32_t substitution_code_point = 0xfffd; - // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the - // conversion is not valid; we defer the check for this to inside the - // multilingual plane check. - uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); - *src_ptr += 6; +// handle a unicode codepoint +// write appropriate values into dest +// src will advance 6 bytes or 12 bytes +// dest will advance a variable amount (return via pointer) +// return true if the unicode codepoint was valid +// We work in little-endian then swap at write time +simdjson_warn_unused +simdjson_inline bool handle_unicode_codepoint(const uint8_t **src_ptr, + uint8_t **dst_ptr, bool allow_replacement) { + // Use the default Unicode Character 'REPLACEMENT CHARACTER' (U+FFFD) + constexpr uint32_t substitution_code_point = 0xfffd; + // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the + // conversion is not valid; we defer the check for this to inside the + // multilingual plane check. + uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); + *src_ptr += 6; - // If we found a high surrogate, we must - // check for low surrogate for characters - // outside the Basic - // Multilingual Plane. - if (code_point >= 0xd800 && code_point < 0xdc00) { - const uint8_t* src_data = *src_ptr; - /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ - if (((src_data[0] << 8) | src_data[1]) != ((static_cast ('\\') << 8) | static_cast ('u'))) { - if (!allow_replacement) { return false; } - code_point = substitution_code_point; - } - else { - uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); + // If we found a high surrogate, we must + // check for low surrogate for characters + // outside the Basic + // Multilingual Plane. + if (code_point >= 0xd800 && code_point < 0xdc00) { + const uint8_t *src_data = *src_ptr; + /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ + if (((src_data[0] << 8) | src_data[1]) != ((static_cast ('\\') << 8) | static_cast ('u'))) { + if(!allow_replacement) { return false; } + code_point = substitution_code_point; + } else { + uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); - // We have already checked that the high surrogate is valid and - // (code_point - 0xd800) < 1024. - // - // Check that code_point_2 is in the range 0xdc00..0xdfff - // and that code_point_2 was parsed from valid hex. - uint32_t low_bit = code_point_2 - 0xdc00; - if (low_bit >> 10) { - if (!allow_replacement) { return false; } - code_point = substitution_code_point; - } - else { - code_point = (((code_point - 0xd800) << 10) | low_bit) + 0x10000; - *src_ptr += 6; - } + // We have already checked that the high surrogate is valid and + // (code_point - 0xd800) < 1024. + // + // Check that code_point_2 is in the range 0xdc00..0xdfff + // and that code_point_2 was parsed from valid hex. + uint32_t low_bit = code_point_2 - 0xdc00; + if (low_bit >> 10) { + if(!allow_replacement) { return false; } + code_point = substitution_code_point; + } else { + code_point = (((code_point - 0xd800) << 10) | low_bit) + 0x10000; + *src_ptr += 6; + } - } - } - else if (code_point >= 0xdc00 && code_point <= 0xdfff) { - // If we encounter a low surrogate (not preceded by a high surrogate) - // then we have an error. - if (!allow_replacement) { return false; } - code_point = substitution_code_point; - } - size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); - *dst_ptr += offset; - return offset > 0; - } + } + } else if (code_point >= 0xdc00 && code_point <= 0xdfff) { + // If we encounter a low surrogate (not preceded by a high surrogate) + // then we have an error. + if(!allow_replacement) { return false; } + code_point = substitution_code_point; + } + size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); + *dst_ptr += offset; + return offset > 0; +} - // handle a unicode codepoint using the wobbly convention - // https://simonsapin.github.io/wtf-8/ - // write appropriate values into dest - // src will advance 6 bytes or 12 bytes - // dest will advance a variable amount (return via pointer) - // return true if the unicode codepoint was valid - // We work in little-endian then swap at write time - simdjson_warn_unused - simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t** src_ptr, - uint8_t** dst_ptr) { - // It is not ideal that this function is nearly identical to handle_unicode_codepoint. - // - // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the - // conversion is not valid; we defer the check for this to inside the - // multilingual plane check. - uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); - *src_ptr += 6; - // If we found a high surrogate, we must - // check for low surrogate for characters - // outside the Basic - // Multilingual Plane. - if (code_point >= 0xd800 && code_point < 0xdc00) { - const uint8_t* src_data = *src_ptr; - /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ - if (((src_data[0] << 8) | src_data[1]) == ((static_cast ('\\') << 8) | static_cast ('u'))) { - uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); - uint32_t low_bit = code_point_2 - 0xdc00; - if ((low_bit >> 10) == 0) { - code_point = - (((code_point - 0xd800) << 10) | low_bit) + 0x10000; - *src_ptr += 6; - } - } - } +// handle a unicode codepoint using the wobbly convention +// https://simonsapin.github.io/wtf-8/ +// write appropriate values into dest +// src will advance 6 bytes or 12 bytes +// dest will advance a variable amount (return via pointer) +// return true if the unicode codepoint was valid +// We work in little-endian then swap at write time +simdjson_warn_unused +simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t **src_ptr, + uint8_t **dst_ptr) { + // It is not ideal that this function is nearly identical to handle_unicode_codepoint. + // + // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the + // conversion is not valid; we defer the check for this to inside the + // multilingual plane check. + uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); + *src_ptr += 6; + // If we found a high surrogate, we must + // check for low surrogate for characters + // outside the Basic + // Multilingual Plane. + if (code_point >= 0xd800 && code_point < 0xdc00) { + const uint8_t *src_data = *src_ptr; + /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ + if (((src_data[0] << 8) | src_data[1]) == ((static_cast ('\\') << 8) | static_cast ('u'))) { + uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); + uint32_t low_bit = code_point_2 - 0xdc00; + if ((low_bit >> 10) == 0) { + code_point = + (((code_point - 0xd800) << 10) | low_bit) + 0x10000; + *src_ptr += 6; + } + } + } - size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); - *dst_ptr += offset; - return offset > 0; - } + size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); + *dst_ptr += offset; + return offset > 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. - */ - simdjson_warn_unused simdjson_inline uint8_t* parse_string(const uint8_t* src, uint8_t* dst, bool allow_replacement) { - while (1) { - // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); - // If the next thing is the end quote, copy and return - if (bs_quote.has_quote_first()) { - // we encountered quotes first. Move dst to point to quotes and exit - return dst + bs_quote.quote_index(); - } - if (bs_quote.has_backslash()) { - /* find out where the backspace is */ - auto bs_dist = bs_quote.backslash_index(); - uint8_t escape_char = src[bs_dist + 1]; - /* we encountered backslash first. Handle backslash */ - if (escape_char == 'u') { - /* move src/dst up to the start; they will be further adjusted - within the unicode codepoint handling code. */ - src += bs_dist; - dst += bs_dist; - if (!handle_unicode_codepoint(&src, &dst, allow_replacement)) { - return nullptr; - } - } - else { - /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and - * write bs_dist+1 characters to output - * note this may reach beyond the part of the buffer we've actually - * seen. I think this is ok */ - uint8_t escape_result = escape_map[escape_char]; - if (escape_result == 0u) { - return nullptr; /* bogus escape value is an error */ - } - dst[bs_dist] = escape_result; - src += bs_dist + 2; - dst += bs_dist + 1; - } - } - else { - /* they are the same. Since they can't co-occur, it means we - * encountered neither. */ - src += backslash_and_quote::BYTES_PROCESSED; - dst += backslash_and_quote::BYTES_PROCESSED; - } - } - } +/** + * 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. + */ +simdjson_warn_unused simdjson_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) { + while (1) { + // Copy the next n bytes, and find the backslash and quote in them. + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); + // If the next thing is the end quote, copy and return + if (bs_quote.has_quote_first()) { + // we encountered quotes first. Move dst to point to quotes and exit + return dst + bs_quote.quote_index(); + } + if (bs_quote.has_backslash()) { + /* find out where the backspace is */ + auto bs_dist = bs_quote.backslash_index(); + uint8_t escape_char = src[bs_dist + 1]; + /* we encountered backslash first. Handle backslash */ + if (escape_char == 'u') { + /* move src/dst up to the start; they will be further adjusted + within the unicode codepoint handling code. */ + src += bs_dist; + dst += bs_dist; + if (!handle_unicode_codepoint(&src, &dst, allow_replacement)) { + return nullptr; + } + } else { + /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and + * write bs_dist+1 characters to output + * note this may reach beyond the part of the buffer we've actually + * seen. I think this is ok */ + uint8_t escape_result = escape_map[escape_char]; + if (escape_result == 0u) { + return nullptr; /* bogus escape value is an error */ + } + dst[bs_dist] = escape_result; + src += bs_dist + 2; + dst += bs_dist + 1; + } + } else { + /* they are the same. Since they can't co-occur, it means we + * encountered neither. */ + src += backslash_and_quote::BYTES_PROCESSED; + dst += backslash_and_quote::BYTES_PROCESSED; + } + } +} - simdjson_warn_unused simdjson_inline uint8_t* parse_wobbly_string(const uint8_t* src, uint8_t* dst) { - // It is not ideal that this function is nearly identical to parse_string. - while (1) { - // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); - // If the next thing is the end quote, copy and return - if (bs_quote.has_quote_first()) { - // we encountered quotes first. Move dst to point to quotes and exit - return dst + bs_quote.quote_index(); - } - if (bs_quote.has_backslash()) { - /* find out where the backspace is */ - auto bs_dist = bs_quote.backslash_index(); - uint8_t escape_char = src[bs_dist + 1]; - /* we encountered backslash first. Handle backslash */ - if (escape_char == 'u') { - /* move src/dst up to the start; they will be further adjusted - within the unicode codepoint handling code. */ - src += bs_dist; - dst += bs_dist; - if (!handle_unicode_codepoint_wobbly(&src, &dst)) { - return nullptr; - } - } - else { - /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and - * write bs_dist+1 characters to output - * note this may reach beyond the part of the buffer we've actually - * seen. I think this is ok */ - uint8_t escape_result = escape_map[escape_char]; - if (escape_result == 0u) { - return nullptr; /* bogus escape value is an error */ - } - dst[bs_dist] = escape_result; - src += bs_dist + 2; - dst += bs_dist + 1; - } - } - else { - /* they are the same. Since they can't co-occur, it means we - * encountered neither. */ - src += backslash_and_quote::BYTES_PROCESSED; - dst += backslash_and_quote::BYTES_PROCESSED; - } - } - } +simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) { + // It is not ideal that this function is nearly identical to parse_string. + while (1) { + // Copy the next n bytes, and find the backslash and quote in them. + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); + // If the next thing is the end quote, copy and return + if (bs_quote.has_quote_first()) { + // we encountered quotes first. Move dst to point to quotes and exit + return dst + bs_quote.quote_index(); + } + if (bs_quote.has_backslash()) { + /* find out where the backspace is */ + auto bs_dist = bs_quote.backslash_index(); + uint8_t escape_char = src[bs_dist + 1]; + /* we encountered backslash first. Handle backslash */ + if (escape_char == 'u') { + /* move src/dst up to the start; they will be further adjusted + within the unicode codepoint handling code. */ + src += bs_dist; + dst += bs_dist; + if (!handle_unicode_codepoint_wobbly(&src, &dst)) { + return nullptr; + } + } else { + /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and + * write bs_dist+1 characters to output + * note this may reach beyond the part of the buffer we've actually + * seen. I think this is ok */ + uint8_t escape_result = escape_map[escape_char]; + if (escape_result == 0u) { + return nullptr; /* bogus escape value is an error */ + } + dst[bs_dist] = escape_result; + src += bs_dist + 2; + dst += bs_dist + 1; + } + } else { + /* they are the same. Since they can't co-occur, it means we + * encountered neither. */ + src += backslash_and_quote::BYTES_PROCESSED; + dst += backslash_and_quote::BYTES_PROCESSED; + } + } +} - } // namespace stringparsing - } // unnamed namespace - } // namespace westmere +} // namespace stringparsing + +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H @@ -39708,58 +41481,58 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { - namespace stage2 { +namespace westmere { +namespace { +namespace stage2 { - class structural_iterator { - public: - const uint8_t* const buf; - uint32_t* next_structural; - dom_parser_implementation& dom_parser; +class structural_iterator { +public: + const uint8_t* const buf; + uint32_t *next_structural; + dom_parser_implementation &dom_parser; - // Start a structural - simdjson_inline structural_iterator(dom_parser_implementation& _dom_parser, size_t start_structural_index) - : buf{ _dom_parser.buf }, - next_structural{ &_dom_parser.structural_indexes[start_structural_index] }, - dom_parser{ _dom_parser } { - } - // Get the buffer position of the current structural character - simdjson_inline const uint8_t* current() { - return &buf[*(next_structural - 1)]; - } - // Get the current structural character - simdjson_inline char current_char() { - return buf[*(next_structural - 1)]; - } - // Get the next structural character without advancing - simdjson_inline char peek_next_char() { - return buf[*next_structural]; - } - simdjson_inline const uint8_t* peek() { - return &buf[*next_structural]; - } - simdjson_inline const uint8_t* advance() { - return &buf[*(next_structural++)]; - } - simdjson_inline char advance_char() { - return buf[*(next_structural++)]; - } - simdjson_inline size_t remaining_len() { - return dom_parser.len - *(next_structural - 1); - } + // Start a structural + simdjson_inline structural_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index) + : buf{_dom_parser.buf}, + next_structural{&_dom_parser.structural_indexes[start_structural_index]}, + dom_parser{_dom_parser} { + } + // Get the buffer position of the current structural character + simdjson_inline const uint8_t* current() { + return &buf[*(next_structural-1)]; + } + // Get the current structural character + simdjson_inline char current_char() { + return buf[*(next_structural-1)]; + } + // Get the next structural character without advancing + simdjson_inline char peek_next_char() { + return buf[*next_structural]; + } + simdjson_inline const uint8_t* peek() { + return &buf[*next_structural]; + } + simdjson_inline const uint8_t* advance() { + return &buf[*(next_structural++)]; + } + simdjson_inline char advance_char() { + return buf[*(next_structural++)]; + } + simdjson_inline size_t remaining_len() { + return dom_parser.len - *(next_structural-1); + } - simdjson_inline bool at_end() { - return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; - } - simdjson_inline bool at_beginning() { - return next_structural == dom_parser.structural_indexes.get(); - } - }; + simdjson_inline bool at_end() { + return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; + } + simdjson_inline bool at_beginning() { + return next_structural == dom_parser.structural_indexes.get(); + } +}; - } // namespace stage2 - } // unnamed namespace - } // namespace westmere +} // namespace stage2 +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_STRUCTURAL_ITERATOR_H @@ -39782,284 +41555,284 @@ namespace simdjson { namespace simdjson { - namespace westmere { - namespace { - namespace stage2 { +namespace westmere { +namespace { +namespace stage2 { - struct tape_builder { - template - simdjson_warn_unused static simdjson_inline error_code parse_document( - dom_parser_implementation& dom_parser, - dom::document& doc) noexcept; +struct tape_builder { + template + simdjson_warn_unused static simdjson_inline error_code parse_document( + dom_parser_implementation &dom_parser, + dom::document &doc) noexcept; - /** Called when a non-empty document starts. */ - simdjson_warn_unused simdjson_inline error_code visit_document_start(json_iterator& iter) noexcept; - /** Called when a non-empty document ends without error. */ - simdjson_warn_unused simdjson_inline error_code visit_document_end(json_iterator& iter) noexcept; + /** Called when a non-empty document starts. */ + simdjson_warn_unused simdjson_inline error_code visit_document_start(json_iterator &iter) noexcept; + /** Called when a non-empty document ends without error. */ + simdjson_warn_unused simdjson_inline error_code visit_document_end(json_iterator &iter) noexcept; - /** Called when a non-empty array starts. */ - simdjson_warn_unused simdjson_inline error_code visit_array_start(json_iterator& iter) noexcept; - /** Called when a non-empty array ends. */ - simdjson_warn_unused simdjson_inline error_code visit_array_end(json_iterator& iter) noexcept; - /** Called when an empty array is found. */ - simdjson_warn_unused simdjson_inline error_code visit_empty_array(json_iterator& iter) noexcept; + /** Called when a non-empty array starts. */ + simdjson_warn_unused simdjson_inline error_code visit_array_start(json_iterator &iter) noexcept; + /** Called when a non-empty array ends. */ + simdjson_warn_unused simdjson_inline error_code visit_array_end(json_iterator &iter) noexcept; + /** Called when an empty array is found. */ + simdjson_warn_unused simdjson_inline error_code visit_empty_array(json_iterator &iter) noexcept; - /** Called when a non-empty object starts. */ - simdjson_warn_unused simdjson_inline error_code visit_object_start(json_iterator& iter) noexcept; - /** - * Called when a key in a field is encountered. - * - * primitive, visit_object_start, visit_empty_object, visit_array_start, or visit_empty_array - * will be called after this with the field value. - */ - simdjson_warn_unused simdjson_inline error_code visit_key(json_iterator& iter, const uint8_t* key) noexcept; - /** Called when a non-empty object ends. */ - simdjson_warn_unused simdjson_inline error_code visit_object_end(json_iterator& iter) noexcept; - /** Called when an empty object is found. */ - simdjson_warn_unused simdjson_inline error_code visit_empty_object(json_iterator& iter) noexcept; + /** Called when a non-empty object starts. */ + simdjson_warn_unused simdjson_inline error_code visit_object_start(json_iterator &iter) noexcept; + /** + * Called when a key in a field is encountered. + * + * primitive, visit_object_start, visit_empty_object, visit_array_start, or visit_empty_array + * will be called after this with the field value. + */ + simdjson_warn_unused simdjson_inline error_code visit_key(json_iterator &iter, const uint8_t *key) noexcept; + /** Called when a non-empty object ends. */ + simdjson_warn_unused simdjson_inline error_code visit_object_end(json_iterator &iter) noexcept; + /** Called when an empty object is found. */ + simdjson_warn_unused simdjson_inline error_code visit_empty_object(json_iterator &iter) noexcept; - /** - * Called when a string, number, boolean or null is found. - */ - simdjson_warn_unused simdjson_inline error_code visit_primitive(json_iterator& iter, const uint8_t* value) noexcept; - /** - * Called when a string, number, boolean or null is found at the top level of a document (i.e. - * when there is no array or object and the entire document is a single string, number, boolean or - * null. - * - * This is separate from primitive() because simdjson's normal primitive parsing routines assume - * there is at least one more token after the value, which is only true in an array or object. - */ - simdjson_warn_unused simdjson_inline error_code visit_root_primitive(json_iterator& iter, const uint8_t* value) noexcept; + /** + * Called when a string, number, boolean or null is found. + */ + simdjson_warn_unused simdjson_inline error_code visit_primitive(json_iterator &iter, const uint8_t *value) noexcept; + /** + * Called when a string, number, boolean or null is found at the top level of a document (i.e. + * when there is no array or object and the entire document is a single string, number, boolean or + * null. + * + * This is separate from primitive() because simdjson's normal primitive parsing routines assume + * there is at least one more token after the value, which is only true in an array or object. + */ + simdjson_warn_unused simdjson_inline error_code visit_root_primitive(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_string(json_iterator& iter, const uint8_t* value, bool key = false) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_number(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_true_atom(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_false_atom(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_null_atom(json_iterator& iter, const uint8_t* value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_string(json_iterator &iter, const uint8_t *value, bool key = false) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_number(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_true_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_false_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_null_atom(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_string(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_number(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_true_atom(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_false_atom(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_null_atom(json_iterator& iter, const uint8_t* value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_string(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_number(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_true_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept; - /** Called each time a new field or element in an array or object is found. */ - simdjson_warn_unused simdjson_inline error_code increment_count(json_iterator& iter) noexcept; + /** Called each time a new field or element in an array or object is found. */ + simdjson_warn_unused simdjson_inline error_code increment_count(json_iterator &iter) noexcept; - /** Next location to write to tape */ - tape_writer tape; - private: - /** Next write location in the string buf for stage 2 parsing */ - uint8_t* current_string_buf_loc; + /** Next location to write to tape */ + tape_writer tape; +private: + /** Next write location in the string buf for stage 2 parsing */ + uint8_t *current_string_buf_loc; - simdjson_inline tape_builder(dom::document& doc) noexcept; + simdjson_inline tape_builder(dom::document &doc) noexcept; - simdjson_inline uint32_t next_tape_index(json_iterator& iter) const noexcept; - simdjson_inline void start_container(json_iterator& iter) noexcept; - simdjson_warn_unused simdjson_inline error_code end_container(json_iterator& iter, internal::tape_type start, internal::tape_type end) noexcept; - simdjson_warn_unused simdjson_inline error_code empty_container(json_iterator& iter, internal::tape_type start, internal::tape_type end) noexcept; - simdjson_inline uint8_t* on_start_string(json_iterator& iter) noexcept; - simdjson_inline void on_end_string(uint8_t* dst) noexcept; - }; // struct tape_builder + simdjson_inline uint32_t next_tape_index(json_iterator &iter) const noexcept; + simdjson_inline void start_container(json_iterator &iter) noexcept; + simdjson_warn_unused simdjson_inline error_code end_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept; + simdjson_warn_unused simdjson_inline error_code empty_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept; + simdjson_inline uint8_t *on_start_string(json_iterator &iter) noexcept; + simdjson_inline void on_end_string(uint8_t *dst) noexcept; +}; // struct tape_builder - template - simdjson_warn_unused simdjson_inline error_code tape_builder::parse_document( - dom_parser_implementation& dom_parser, - dom::document& doc) noexcept { - dom_parser.doc = &doc; - json_iterator iter(dom_parser, STREAMING ? dom_parser.next_structural_index : 0); - tape_builder builder(doc); - return iter.walk_document(builder); - } +template +simdjson_warn_unused simdjson_inline error_code tape_builder::parse_document( + dom_parser_implementation &dom_parser, + dom::document &doc) noexcept { + dom_parser.doc = &doc; + json_iterator iter(dom_parser, STREAMING ? dom_parser.next_structural_index : 0); + tape_builder builder(doc); + return iter.walk_document(builder); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_primitive(json_iterator& iter, const uint8_t* value) noexcept { - return iter.visit_root_primitive(*this, value); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_primitive(json_iterator& iter, const uint8_t* value) noexcept { - return iter.visit_primitive(*this, value); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_object(json_iterator& iter) noexcept { - return empty_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_array(json_iterator& iter) noexcept { - return empty_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_primitive(json_iterator &iter, const uint8_t *value) noexcept { + return iter.visit_root_primitive(*this, value); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_primitive(json_iterator &iter, const uint8_t *value) noexcept { + return iter.visit_primitive(*this, value); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_object(json_iterator &iter) noexcept { + return empty_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_array(json_iterator &iter) noexcept { + return empty_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_start(json_iterator& iter) noexcept { - start_container(iter); - return SUCCESS; - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_start(json_iterator& iter) noexcept { - start_container(iter); - return SUCCESS; - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_start(json_iterator& iter) noexcept { - start_container(iter); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_start(json_iterator &iter) noexcept { + start_container(iter); + return SUCCESS; +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_start(json_iterator &iter) noexcept { + start_container(iter); + return SUCCESS; +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_start(json_iterator &iter) noexcept { + start_container(iter); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_end(json_iterator& iter) noexcept { - return end_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_end(json_iterator& iter) noexcept { - return end_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_end(json_iterator& iter) noexcept { - constexpr uint32_t start_tape_index = 0; - tape.append(start_tape_index, internal::tape_type::ROOT); - tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter), internal::tape_type::ROOT); - return SUCCESS; - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_key(json_iterator& iter, const uint8_t* key) noexcept { - return visit_string(iter, key, true); - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_end(json_iterator &iter) noexcept { + return end_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_end(json_iterator &iter) noexcept { + return end_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_end(json_iterator &iter) noexcept { + constexpr uint32_t start_tape_index = 0; + tape.append(start_tape_index, internal::tape_type::ROOT); + tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter), internal::tape_type::ROOT); + return SUCCESS; +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_key(json_iterator &iter, const uint8_t *key) noexcept { + return visit_string(iter, key, true); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::increment_count(json_iterator& iter) noexcept { - iter.dom_parser.open_containers[iter.depth].count++; // we have a key value pair in the object at parser.dom_parser.depth - 1 - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::increment_count(json_iterator &iter) noexcept { + iter.dom_parser.open_containers[iter.depth].count++; // we have a key value pair in the object at parser.dom_parser.depth - 1 + return SUCCESS; +} - simdjson_inline tape_builder::tape_builder(dom::document& doc) noexcept : tape{ doc.tape.get() }, current_string_buf_loc{ doc.string_buf.get() } {} +simdjson_inline tape_builder::tape_builder(dom::document &doc) noexcept : tape{doc.tape.get()}, current_string_buf_loc{doc.string_buf.get()} {} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_string(json_iterator& iter, const uint8_t* value, bool key) noexcept { - iter.log_value(key ? "key" : "string"); - uint8_t* dst = on_start_string(iter); - dst = stringparsing::parse_string(value + 1, dst, false); // We do not allow replacement when the escape characters are invalid. - if (dst == nullptr) { - iter.log_error("Invalid escape in string"); - return STRING_ERROR; - } - on_end_string(dst); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_string(json_iterator &iter, const uint8_t *value, bool key) noexcept { + iter.log_value(key ? "key" : "string"); + uint8_t *dst = on_start_string(iter); + dst = stringparsing::parse_string(value+1, dst, false); // We do not allow replacement when the escape characters are invalid. + if (dst == nullptr) { + iter.log_error("Invalid escape in string"); + return STRING_ERROR; + } + on_end_string(dst); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_string(json_iterator& iter, const uint8_t* value) noexcept { - return visit_string(iter, value); - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_string(json_iterator &iter, const uint8_t *value) noexcept { + return visit_string(iter, value); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_number(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("number"); - return numberparsing::parse_number(value, tape); - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_number(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("number"); + return numberparsing::parse_number(value, tape); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_number(json_iterator& iter, const uint8_t* value) noexcept { - // - // We need to make a copy to make sure that the string is space terminated. - // This is not about padding the input, which should already padded up - // to len + SIMDJSON_PADDING. However, we have no control at this stage - // on how the padding was done. What if the input string was padded with nulls? - // It is quite common for an input string to have an extra null character (C string). - // We do not want to allow 9\0 (where \0 is the null character) inside a JSON - // document, but the string "9\0" by itself is fine. So we make a copy and - // pad the input with spaces when we know that there is just one input element. - // This copy is relatively expensive, but it will almost never be called in - // practice unless you are in the strange scenario where you have many JSON - // documents made of single atoms. - // - std::unique_ptrcopy(new (std::nothrow) uint8_t[iter.remaining_len() + SIMDJSON_PADDING]); - if (copy.get() == nullptr) { return MEMALLOC; } - std::memcpy(copy.get(), value, iter.remaining_len()); - std::memset(copy.get() + iter.remaining_len(), ' ', SIMDJSON_PADDING); - error_code error = visit_number(iter, copy.get()); - return error; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_number(json_iterator &iter, const uint8_t *value) noexcept { + // + // We need to make a copy to make sure that the string is space terminated. + // This is not about padding the input, which should already padded up + // to len + SIMDJSON_PADDING. However, we have no control at this stage + // on how the padding was done. What if the input string was padded with nulls? + // It is quite common for an input string to have an extra null character (C string). + // We do not want to allow 9\0 (where \0 is the null character) inside a JSON + // document, but the string "9\0" by itself is fine. So we make a copy and + // pad the input with spaces when we know that there is just one input element. + // This copy is relatively expensive, but it will almost never be called in + // practice unless you are in the strange scenario where you have many JSON + // documents made of single atoms. + // + std::unique_ptrcopy(new (std::nothrow) uint8_t[iter.remaining_len() + SIMDJSON_PADDING]); + if (copy.get() == nullptr) { return MEMALLOC; } + std::memcpy(copy.get(), value, iter.remaining_len()); + std::memset(copy.get() + iter.remaining_len(), ' ', SIMDJSON_PADDING); + error_code error = visit_number(iter, copy.get()); + return error; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_true_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("true"); - if (!atomparsing::is_valid_true_atom(value)) { return T_ATOM_ERROR; } - tape.append(0, internal::tape_type::TRUE_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_true_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("true"); + if (!atomparsing::is_valid_true_atom(value)) { return T_ATOM_ERROR; } + tape.append(0, internal::tape_type::TRUE_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_true_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("true"); - if (!atomparsing::is_valid_true_atom(value, iter.remaining_len())) { return T_ATOM_ERROR; } - tape.append(0, internal::tape_type::TRUE_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_true_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("true"); + if (!atomparsing::is_valid_true_atom(value, iter.remaining_len())) { return T_ATOM_ERROR; } + tape.append(0, internal::tape_type::TRUE_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_false_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("false"); - if (!atomparsing::is_valid_false_atom(value)) { return F_ATOM_ERROR; } - tape.append(0, internal::tape_type::FALSE_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_false_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("false"); + if (!atomparsing::is_valid_false_atom(value)) { return F_ATOM_ERROR; } + tape.append(0, internal::tape_type::FALSE_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_false_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("false"); - if (!atomparsing::is_valid_false_atom(value, iter.remaining_len())) { return F_ATOM_ERROR; } - tape.append(0, internal::tape_type::FALSE_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("false"); + if (!atomparsing::is_valid_false_atom(value, iter.remaining_len())) { return F_ATOM_ERROR; } + tape.append(0, internal::tape_type::FALSE_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_null_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("null"); - if (!atomparsing::is_valid_null_atom(value)) { return N_ATOM_ERROR; } - tape.append(0, internal::tape_type::NULL_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_null_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("null"); + if (!atomparsing::is_valid_null_atom(value)) { return N_ATOM_ERROR; } + tape.append(0, internal::tape_type::NULL_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_null_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("null"); - if (!atomparsing::is_valid_null_atom(value, iter.remaining_len())) { return N_ATOM_ERROR; } - tape.append(0, internal::tape_type::NULL_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("null"); + if (!atomparsing::is_valid_null_atom(value, iter.remaining_len())) { return N_ATOM_ERROR; } + tape.append(0, internal::tape_type::NULL_VALUE); + return SUCCESS; +} - // private: +// private: - simdjson_inline uint32_t tape_builder::next_tape_index(json_iterator& iter) const noexcept { - return uint32_t(tape.next_tape_loc - iter.dom_parser.doc->tape.get()); - } +simdjson_inline uint32_t tape_builder::next_tape_index(json_iterator &iter) const noexcept { + return uint32_t(tape.next_tape_loc - iter.dom_parser.doc->tape.get()); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::empty_container(json_iterator& iter, internal::tape_type start, internal::tape_type end) noexcept { - auto start_index = next_tape_index(iter); - tape.append(start_index + 2, start); - tape.append(start_index, end); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::empty_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept { + auto start_index = next_tape_index(iter); + tape.append(start_index+2, start); + tape.append(start_index, end); + return SUCCESS; +} - simdjson_inline void tape_builder::start_container(json_iterator& iter) noexcept { - iter.dom_parser.open_containers[iter.depth].tape_index = next_tape_index(iter); - iter.dom_parser.open_containers[iter.depth].count = 0; - tape.skip(); // We don't actually *write* the start element until the end. - } +simdjson_inline void tape_builder::start_container(json_iterator &iter) noexcept { + iter.dom_parser.open_containers[iter.depth].tape_index = next_tape_index(iter); + iter.dom_parser.open_containers[iter.depth].count = 0; + tape.skip(); // We don't actually *write* the start element until the end. +} - simdjson_warn_unused simdjson_inline error_code tape_builder::end_container(json_iterator& iter, internal::tape_type start, internal::tape_type end) noexcept { - // Write the ending tape element, pointing at the start location - const uint32_t start_tape_index = iter.dom_parser.open_containers[iter.depth].tape_index; - tape.append(start_tape_index, end); - // Write the start tape element, pointing at the end location (and including count) - // count can overflow if it exceeds 24 bits... so we saturate - // the convention being that a cnt of 0xffffff or more is undetermined in value (>= 0xffffff). - const uint32_t count = iter.dom_parser.open_containers[iter.depth].count; - const uint32_t cntsat = count > 0xFFFFFF ? 0xFFFFFF : count; - tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter) | (uint64_t(cntsat) << 32), start); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::end_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept { + // Write the ending tape element, pointing at the start location + const uint32_t start_tape_index = iter.dom_parser.open_containers[iter.depth].tape_index; + tape.append(start_tape_index, end); + // Write the start tape element, pointing at the end location (and including count) + // count can overflow if it exceeds 24 bits... so we saturate + // the convention being that a cnt of 0xffffff or more is undetermined in value (>= 0xffffff). + const uint32_t count = iter.dom_parser.open_containers[iter.depth].count; + const uint32_t cntsat = count > 0xFFFFFF ? 0xFFFFFF : count; + tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter) | (uint64_t(cntsat) << 32), start); + return SUCCESS; +} - simdjson_inline uint8_t* tape_builder::on_start_string(json_iterator& iter) noexcept { - // we advance the point, accounting for the fact that we have a NULL termination - tape.append(current_string_buf_loc - iter.dom_parser.doc->string_buf.get(), internal::tape_type::STRING); - return current_string_buf_loc + sizeof(uint32_t); - } +simdjson_inline uint8_t *tape_builder::on_start_string(json_iterator &iter) noexcept { + // we advance the point, accounting for the fact that we have a NULL termination + tape.append(current_string_buf_loc - iter.dom_parser.doc->string_buf.get(), internal::tape_type::STRING); + return current_string_buf_loc + sizeof(uint32_t); +} - simdjson_inline void tape_builder::on_end_string(uint8_t* dst) noexcept { - uint32_t str_length = uint32_t(dst - (current_string_buf_loc + sizeof(uint32_t))); - // TODO check for overflow in case someone has a crazy string (>=4GB?) - // But only add the overflow check when the document itself exceeds 4GB - // Currently unneeded because we refuse to parse docs larger or equal to 4GB. - memcpy(current_string_buf_loc, &str_length, sizeof(uint32_t)); - // NULL termination is still handy if you expect all your strings to - // be NULL terminated? It comes at a small cost - *dst = 0; - current_string_buf_loc = dst + 1; - } +simdjson_inline void tape_builder::on_end_string(uint8_t *dst) noexcept { + uint32_t str_length = uint32_t(dst - (current_string_buf_loc + sizeof(uint32_t))); + // TODO check for overflow in case someone has a crazy string (>=4GB?) + // But only add the overflow check when the document itself exceeds 4GB + // Currently unneeded because we refuse to parse docs larger or equal to 4GB. + memcpy(current_string_buf_loc, &str_length, sizeof(uint32_t)); + // NULL termination is still handy if you expect all your strings to + // be NULL terminated? It comes at a small cost + *dst = 0; + current_string_buf_loc = dst + 1; +} - } // namespace stage2 - } // unnamed namespace - } // namespace westmere +} // namespace stage2 +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_BUILDER_H @@ -40071,105 +41844,105 @@ namespace simdjson { // namespace simdjson { - namespace westmere { +namespace westmere { - simdjson_warn_unused error_code implementation::create_dom_parser_implementation( - size_t capacity, - size_t max_depth, - std::unique_ptr& dst - ) const noexcept { - dst.reset(new (std::nothrow) dom_parser_implementation()); - if (!dst) { return MEMALLOC; } - if (auto err = dst->set_capacity(capacity)) - return err; - if (auto err = dst->set_max_depth(max_depth)) - return err; - return SUCCESS; - } +simdjson_warn_unused error_code implementation::create_dom_parser_implementation( + size_t capacity, + size_t max_depth, + std::unique_ptr& dst +) const noexcept { + dst.reset( new (std::nothrow) dom_parser_implementation() ); + if (!dst) { return MEMALLOC; } + if (auto err = dst->set_capacity(capacity)) + return err; + if (auto err = dst->set_max_depth(max_depth)) + return err; + return SUCCESS; +} - namespace { +namespace { - using namespace simd; +using namespace simd; - simdjson_inline json_character_block json_character_block::classify(const simd::simd8x64& in) { - // These lookups rely on the fact that anything < 127 will match the lower 4 bits, which is why - // we can't use the generic lookup_16. - auto whitespace_table = simd8::repeat_16(' ', 100, 100, 100, 17, 100, 113, 2, 100, '\t', '\n', 112, 100, '\r', 100, 100); +simdjson_inline json_character_block json_character_block::classify(const simd::simd8x64& in) { + // These lookups rely on the fact that anything < 127 will match the lower 4 bits, which is why + // we can't use the generic lookup_16. + auto whitespace_table = simd8::repeat_16(' ', 100, 100, 100, 17, 100, 113, 2, 100, '\t', '\n', 112, 100, '\r', 100, 100); - // The 6 operators (:,[]{}) have these values: - // - // , 2C - // : 3A - // [ 5B - // { 7B - // ] 5D - // } 7D - // - // If you use | 0x20 to turn [ and ] into { and }, the lower 4 bits of each character is unique. - // We exploit this, using a simd 4-bit lookup to tell us which character match against, and then - // match it (against | 0x20). - // - // To prevent recognizing other characters, everything else gets compared with 0, which cannot - // match due to the | 0x20. - // - // NOTE: Due to the | 0x20, this ALSO treats and (control characters 0C and 1A) like , - // and :. This gets caught in stage 2, which checks the actual character to ensure the right - // operators are in the right places. - const auto op_table = simd8::repeat_16( - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, ':', '{', // : = 3A, [ = 5B, { = 7B - ',', '}', 0, 0 // , = 2C, ] = 5D, } = 7D - ); + // The 6 operators (:,[]{}) have these values: + // + // , 2C + // : 3A + // [ 5B + // { 7B + // ] 5D + // } 7D + // + // If you use | 0x20 to turn [ and ] into { and }, the lower 4 bits of each character is unique. + // We exploit this, using a simd 4-bit lookup to tell us which character match against, and then + // match it (against | 0x20). + // + // To prevent recognizing other characters, everything else gets compared with 0, which cannot + // match due to the | 0x20. + // + // NOTE: Due to the | 0x20, this ALSO treats and (control characters 0C and 1A) like , + // and :. This gets caught in stage 2, which checks the actual character to ensure the right + // operators are in the right places. + const auto op_table = simd8::repeat_16( + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, ':', '{', // : = 3A, [ = 5B, { = 7B + ',', '}', 0, 0 // , = 2C, ] = 5D, } = 7D + ); - // We compute whitespace and op separately. If the code later only use one or the - // other, given the fact that all functions are aggressively inlined, we can - // hope that useless computations will be omitted. This is namely case when - // minifying (we only need whitespace). + // We compute whitespace and op separately. If the code later only use one or the + // other, given the fact that all functions are aggressively inlined, we can + // hope that useless computations will be omitted. This is namely case when + // minifying (we only need whitespace). - const uint64_t whitespace = in.eq({ - _mm_shuffle_epi8(whitespace_table, in.chunks[0]), - _mm_shuffle_epi8(whitespace_table, in.chunks[1]), - _mm_shuffle_epi8(whitespace_table, in.chunks[2]), - _mm_shuffle_epi8(whitespace_table, in.chunks[3]) - }); - // Turn [ and ] into { and } - const simd8x64 curlified{ - in.chunks[0] | 0x20, - in.chunks[1] | 0x20, - in.chunks[2] | 0x20, - in.chunks[3] | 0x20 - }; - const uint64_t op = curlified.eq({ - _mm_shuffle_epi8(op_table, in.chunks[0]), - _mm_shuffle_epi8(op_table, in.chunks[1]), - _mm_shuffle_epi8(op_table, in.chunks[2]), - _mm_shuffle_epi8(op_table, in.chunks[3]) - }); - return { whitespace, op }; - } + const uint64_t whitespace = in.eq({ + _mm_shuffle_epi8(whitespace_table, in.chunks[0]), + _mm_shuffle_epi8(whitespace_table, in.chunks[1]), + _mm_shuffle_epi8(whitespace_table, in.chunks[2]), + _mm_shuffle_epi8(whitespace_table, in.chunks[3]) + }); + // Turn [ and ] into { and } + const simd8x64 curlified{ + in.chunks[0] | 0x20, + in.chunks[1] | 0x20, + in.chunks[2] | 0x20, + in.chunks[3] | 0x20 + }; + const uint64_t op = curlified.eq({ + _mm_shuffle_epi8(op_table, in.chunks[0]), + _mm_shuffle_epi8(op_table, in.chunks[1]), + _mm_shuffle_epi8(op_table, in.chunks[2]), + _mm_shuffle_epi8(op_table, in.chunks[3]) + }); + return { whitespace, op }; +} - simdjson_inline bool is_ascii(const simd8x64& input) { - return input.reduce_or().is_ascii(); - } +simdjson_inline bool is_ascii(const simd8x64& input) { + return input.reduce_or().is_ascii(); +} - simdjson_unused simdjson_inline simd8 must_be_continuation(const simd8 prev1, const simd8 prev2, const simd8 prev3) { - simd8 is_second_byte = prev1.saturating_sub(0xc0u - 1); // Only 11______ will be > 0 - simd8 is_third_byte = prev2.saturating_sub(0xe0u - 1); // Only 111_____ will be > 0 - simd8 is_fourth_byte = prev3.saturating_sub(0xf0u - 1); // Only 1111____ will be > 0 - // Caller requires a bool (all 1's). All values resulting from the subtraction will be <= 64, so signed comparison is fine. - return simd8(is_second_byte | is_third_byte | is_fourth_byte) > int8_t(0); - } +simdjson_unused simdjson_inline simd8 must_be_continuation(const simd8 prev1, const simd8 prev2, const simd8 prev3) { + simd8 is_second_byte = prev1.saturating_sub(0xc0u-1); // Only 11______ will be > 0 + simd8 is_third_byte = prev2.saturating_sub(0xe0u-1); // Only 111_____ will be > 0 + simd8 is_fourth_byte = prev3.saturating_sub(0xf0u-1); // Only 1111____ will be > 0 + // Caller requires a bool (all 1's). All values resulting from the subtraction will be <= 64, so signed comparison is fine. + return simd8(is_second_byte | is_third_byte | is_fourth_byte) > int8_t(0); +} - simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { - simd8 is_third_byte = prev2.saturating_sub(0xe0u - 0x80); // Only 111_____ will be >= 0x80 - simd8 is_fourth_byte = prev3.saturating_sub(0xf0u - 0x80); // Only 1111____ will be >= 0x80 - return is_third_byte | is_fourth_byte; - } +simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { + simd8 is_third_byte = prev2.saturating_sub(0xe0u-0x80); // Only 111_____ will be >= 0x80 + simd8 is_fourth_byte = prev3.saturating_sub(0xf0u-0x80); // Only 1111____ will be >= 0x80 + return is_third_byte | is_fourth_byte; +} - } // unnamed namespace - } // namespace westmere +} // unnamed namespace +} // namespace westmere } // namespace simdjson // @@ -40181,45 +41954,45 @@ namespace simdjson { // namespace simdjson { - namespace westmere { +namespace westmere { - simdjson_warn_unused error_code implementation::minify(const uint8_t* buf, size_t len, uint8_t* dst, size_t& dst_len) const noexcept { - return westmere::stage1::json_minifier::minify<64>(buf, len, dst, dst_len); - } +simdjson_warn_unused error_code implementation::minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept { + return westmere::stage1::json_minifier::minify<64>(buf, len, dst, dst_len); +} - simdjson_warn_unused error_code dom_parser_implementation::stage1(const uint8_t* _buf, size_t _len, stage1_mode streaming) noexcept { - this->buf = _buf; - this->len = _len; - return westmere::stage1::json_structural_indexer::index<64>(_buf, _len, *this, streaming); - } +simdjson_warn_unused error_code dom_parser_implementation::stage1(const uint8_t *_buf, size_t _len, stage1_mode streaming) noexcept { + this->buf = _buf; + this->len = _len; + return westmere::stage1::json_structural_indexer::index<64>(_buf, _len, *this, streaming); +} - simdjson_warn_unused bool implementation::validate_utf8(const char* buf, size_t len) const noexcept { - return westmere::stage1::generic_validate_utf8(buf, len); - } +simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept { + return westmere::stage1::generic_validate_utf8(buf,len); +} - simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document& _doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); - } +simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept { + return stage2::tape_builder::parse_document(*this, _doc); +} - simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document& _doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); - } +simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept { + return stage2::tape_builder::parse_document(*this, _doc); +} - simdjson_warn_unused uint8_t* dom_parser_implementation::parse_string(const uint8_t* src, uint8_t* dst, bool replacement_char) const noexcept { - return westmere::stringparsing::parse_string(src, dst, replacement_char); - } +SIMDJSON_NO_SANITIZE_MEMORY +simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool replacement_char) const noexcept { + return westmere::stringparsing::parse_string(src, dst, replacement_char); +} - simdjson_warn_unused uint8_t* dom_parser_implementation::parse_wobbly_string(const uint8_t* src, uint8_t* dst) const noexcept { - return westmere::stringparsing::parse_wobbly_string(src, dst); - } +simdjson_warn_unused uint8_t *dom_parser_implementation::parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept { + return westmere::stringparsing::parse_wobbly_string(src, dst); +} - simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t* _buf, size_t _len, dom::document& _doc) noexcept { - auto error = stage1(_buf, _len, stage1_mode::regular); - if (error) { return error; } - return stage2(_doc); - } - - } // namespace westmere +simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept { + auto error = stage1(_buf, _len, stage1_mode::regular); + if (error) { return error; } + return stage2(_doc); +} +} // namespace westmere } // namespace simdjson /* including simdjson/westmere/end.h: #include */ @@ -40239,6 +42012,6189 @@ SIMDJSON_UNTARGET_REGION #endif // SIMDJSON_SRC_WESTMERE_CPP /* end file westmere.cpp */ #endif +#if SIMDJSON_IMPLEMENTATION_LASX +/* including lasx.cpp: #include */ +/* begin file lasx.cpp */ +#ifndef SIMDJSON_SRC_LASX_CPP +#define SIMDJSON_SRC_LASX_CPP + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +/* including simdjson/lasx.h: #include */ +/* 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 // 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 struct simd8; +template 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 +#include + +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); +} + +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t *result) { + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); +} + +} // 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 + +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 + 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(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; + + // Forward-declared so they can be used by splat and friends. + template + struct simd8; + + template> + struct base8: base> { + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m256i _value) : base>(_value) {} + + friend simdjson_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return __lasx_xvseq_b(lhs, rhs); } + + static const int SIZE = sizeof(base>::value); + + template + simdjson_inline simd8 prev(const simd8 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: base8 { + static simdjson_inline simd8 splat(bool _value) { return __lasx_xvreplgr2vr_b(uint8_t(-(!!_value))); } + + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m256i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(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 operator~() const { return *this ^ true; } + }; + + template + struct base8_numeric: base8 { + static simdjson_inline simd8 splat(T _value) { + return __lasx_xvreplgr2vr_b(_value); + } + static simdjson_inline simd8 zero() { return __lasx_xvldi(0); } + static simdjson_inline simd8 load(const T values[32]) { + return __lasx_xvld(reinterpret_cast(values), 0); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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( + 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() {} + simdjson_inline base8_numeric(const __m256i _value) : base8(_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 operator+(const simd8 other) const { return __lasx_xvadd_b(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return __lasx_xvsub_b(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } + + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } + + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 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 + 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(reinterpret_cast(pshufb_combine_table)), pop1 * 8); + __m256i maskhi = __lasx_xvldx(reinterpret_cast(reinterpret_cast(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(output), 0); + uint64_t value3 = __lasx_xvpickve2gr_du(answer, 2); + uint64_t value4 = __lasx_xvpickve2gr_du(answer, 3); + uint64_t *pos = reinterpret_cast(reinterpret_cast(output) + 16 - (pop1 + pop2) / 2); + pos[0] = value3; + pos[1] = value4; + } + + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; + + // Signed bytes + template<> + struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( + 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 max_val(const simd8 other) const { return __lasx_xvmax_b(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return __lasx_xvmin_b(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return __lasx_xvslt_b(other, *this); } + simdjson_inline simd8 operator<(const simd8 other) const { return __lasx_xvslt_b(*this, other); } + }; + + // Unsigned bytes + template<> + struct simd8: base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( + 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 saturating_add(const simd8 other) const { return __lasx_xvsadd_bu(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return __lasx_xvssub_bu(*this, other); } + + // Order-specific operations + simdjson_inline simd8 max_val(const simd8 other) const { return __lasx_xvmax_bu(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return __lasx_xvmin_bu(other, *this); } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } + simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } + simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } + simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } + + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } + simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } + simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } + simdjson_inline simd8 any_bits_set(simd8 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 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 bits) const { return !bits_not_set_anywhere(bits); } + template + simdjson_inline simd8 shr() const { return simd8(__lasx_xvsrli_b(*this, N)); } + template + simdjson_inline simd8 shl() const { return simd8(__lasx_xvslli_b(*this, N)); } + }; + + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 2, "LASX kernel should use two registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } + + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed + + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1) : chunks{chunk0, chunk1} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::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)*0); + this->chunks[1].store(ptr+sizeof(simd8)*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 reduce_or() const { + return this->chunks[0] | this->chunks[1]; + } + + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] == mask, + this->chunks[1] == mask + ).to_bitmask(); + } + + simdjson_inline uint64_t eq(const simd8x64 &other) const { + return simd8x64( + 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 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] <= mask, + this->chunks[1] <= mask + ).to_bitmask(); + } + }; // struct simd8x64 + +} // 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 v(src); + v.store(dst); + return { + static_cast((v == '\\').to_bitmask()), // bs_bits + static_cast((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 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + static_cast((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 + +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; } + + +// 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); +} + +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; } +} + +} // 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_containers{}; + /** Whether each open container is a [ or { */ + std::unique_ptr 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) { return CAPACITY; } + // Stage 1 index output + size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; + 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 { + // 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 : public internal::implementation_simdjson_result_base { + * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} + * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} + * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} + * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} + * // Your extra methods here + * } + * + * Then any method returning simdjson_result will be chainable with your methods. + */ +template +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/internal/numberparsing_tables.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +#include +#include + +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(negative)) << 63); + std::memcpy(&d, &mantissa, sizeof(d)); + return d; +} + +// 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; +} + +// 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(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::max)() || *outDouble < std::numeric_limits::lowest()); +} + +static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) { + *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(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::max)() || *outDouble < std::numeric_limits::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); +} + +template +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(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(c - '0') <= 9; +} + +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; + +#ifdef SIMDJSON_SWAR_NUMBER_PARSING +#if SIMDJSON_SWAR_NUMBER_PARSING + // this helps if we have lots of decimals! + // this turns out to be frequent enough. + if (is_made_of_eight_digits_fast(p)) { + i = i * 100000000 + parse_eight_digits_unrolled(p); + p += 8; + } +#endif // SIMDJSON_SWAR_NUMBER_PARSING +#endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING + // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) + if (parse_digit(*p, i)) { ++p; } + while (parse_digit(*p, i)) { p++; } + 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 */ +static 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 +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 +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 +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 parse_unsigned(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t * src) 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 +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)) { 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 INVALID_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 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; + 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); + // 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 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 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; + 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); + // 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 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; + 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); + // 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 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 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; + while (parse_digit(*src, i)) { src++; } + + // 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { + return (*src == '-'); +} + +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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 get_number_type(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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("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("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 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +} // 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 inline implementation +// + +template +simdjson_inline void implementation_simdjson_result_base::tie(T &value, error_code &error) && noexcept { + error = this->second; + if (!error) { + value = std::forward>(*this).first; + } +} + +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T &value) && noexcept { + error_code error; + std::forward>(*this).tie(value, error); + return error; +} + +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { + return this->second; +} + + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} + +#if SIMDJSON_EXCEPTIONS + +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} + +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + +template +simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return this->first; +} + +template +simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { + return std::forward>(*this).take_value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return std::forward(this->first); +} + +template +simdjson_inline implementation_simdjson_result_base::operator T&&() && noexcept(false) { + return std::forward>(*this).take_value(); +} + +#endif // SIMDJSON_EXCEPTIONS + +template +simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { + return this->first; +} + +template +simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { + return this->first; +} + +template +simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { + return std::forward(this->first); +} + +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value, error_code error) noexcept + : first{std::forward(value)}, second{error} {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept + : implementation_simdjson_result_base(T{}, error) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value) noexcept + : implementation_simdjson_result_base(std::forward(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 */ +/* including simdjson/lasx/implementation.h: #include */ +/* 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& 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 */ + +/* including simdjson/lasx/begin.h: #include */ +/* begin file simdjson/lasx/begin.h */ +/* defining SIMDJSON_IMPLEMENTATION to "lasx" */ +#define SIMDJSON_IMPLEMENTATION lasx +#include // 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 struct simd8; +template 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 +#include + +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); +} + +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t *result) { + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); +} + +} // 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 + +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 + 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(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; + + // Forward-declared so they can be used by splat and friends. + template + struct simd8; + + template> + struct base8: base> { + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m256i _value) : base>(_value) {} + + friend simdjson_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return __lasx_xvseq_b(lhs, rhs); } + + static const int SIZE = sizeof(base>::value); + + template + simdjson_inline simd8 prev(const simd8 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: base8 { + static simdjson_inline simd8 splat(bool _value) { return __lasx_xvreplgr2vr_b(uint8_t(-(!!_value))); } + + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m256i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(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 operator~() const { return *this ^ true; } + }; + + template + struct base8_numeric: base8 { + static simdjson_inline simd8 splat(T _value) { + return __lasx_xvreplgr2vr_b(_value); + } + static simdjson_inline simd8 zero() { return __lasx_xvldi(0); } + static simdjson_inline simd8 load(const T values[32]) { + return __lasx_xvld(reinterpret_cast(values), 0); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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( + 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() {} + simdjson_inline base8_numeric(const __m256i _value) : base8(_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 operator+(const simd8 other) const { return __lasx_xvadd_b(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return __lasx_xvsub_b(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } + + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } + + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 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 + 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(reinterpret_cast(pshufb_combine_table)), pop1 * 8); + __m256i maskhi = __lasx_xvldx(reinterpret_cast(reinterpret_cast(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(output), 0); + uint64_t value3 = __lasx_xvpickve2gr_du(answer, 2); + uint64_t value4 = __lasx_xvpickve2gr_du(answer, 3); + uint64_t *pos = reinterpret_cast(reinterpret_cast(output) + 16 - (pop1 + pop2) / 2); + pos[0] = value3; + pos[1] = value4; + } + + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; + + // Signed bytes + template<> + struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( + 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 max_val(const simd8 other) const { return __lasx_xvmax_b(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return __lasx_xvmin_b(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return __lasx_xvslt_b(other, *this); } + simdjson_inline simd8 operator<(const simd8 other) const { return __lasx_xvslt_b(*this, other); } + }; + + // Unsigned bytes + template<> + struct simd8: base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( + 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 saturating_add(const simd8 other) const { return __lasx_xvsadd_bu(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return __lasx_xvssub_bu(*this, other); } + + // Order-specific operations + simdjson_inline simd8 max_val(const simd8 other) const { return __lasx_xvmax_bu(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return __lasx_xvmin_bu(other, *this); } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } + simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } + simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } + simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } + + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } + simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } + simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } + simdjson_inline simd8 any_bits_set(simd8 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 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 bits) const { return !bits_not_set_anywhere(bits); } + template + simdjson_inline simd8 shr() const { return simd8(__lasx_xvsrli_b(*this, N)); } + template + simdjson_inline simd8 shl() const { return simd8(__lasx_xvslli_b(*this, N)); } + }; + + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 2, "LASX kernel should use two registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } + + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed + + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1) : chunks{chunk0, chunk1} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::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)*0); + this->chunks[1].store(ptr+sizeof(simd8)*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 reduce_or() const { + return this->chunks[0] | this->chunks[1]; + } + + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] == mask, + this->chunks[1] == mask + ).to_bitmask(); + } + + simdjson_inline uint64_t eq(const simd8x64 &other) const { + return simd8x64( + 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 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] <= mask, + this->chunks[1] <= mask + ).to_bitmask(); + } + }; // struct simd8x64 + +} // 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 v(src); + v.store(dst); + return { + static_cast((v == '\\').to_bitmask()), // bs_bits + static_cast((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 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + static_cast((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 generic/amalgamated.h for lasx: #include */ +/* begin file generic/amalgamated.h for lasx */ +#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_SRC_GENERIC_DEPENDENCIES_H) +#error generic/dependencies.h must be included before generic/amalgamated.h! +#endif + +/* including generic/base.h for lasx: #include */ +/* begin file generic/base.h for lasx */ +#ifndef SIMDJSON_SRC_GENERIC_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_SRC_GENERIC_BASE_H */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace lasx { +namespace { + +struct json_character_block; + +} // unnamed namespace +} // namespace lasx +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_BASE_H +/* end file generic/base.h for lasx */ +/* including generic/dom_parser_implementation.h for lasx: #include */ +/* begin file generic/dom_parser_implementation.h for lasx */ +#ifndef SIMDJSON_SRC_GENERIC_DOM_PARSER_IMPLEMENTATION_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_SRC_GENERIC_DOM_PARSER_IMPLEMENTATION_H */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +// Interface a dom parser implementation must fulfill +namespace simdjson { +namespace lasx { +namespace { + +simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3); +simdjson_inline bool is_ascii(const simd8x64& input); + +} // unnamed namespace +} // namespace lasx +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_DOM_PARSER_IMPLEMENTATION_H +/* end file generic/dom_parser_implementation.h for lasx */ +/* including generic/json_character_block.h for lasx: #include */ +/* begin file generic/json_character_block.h for lasx */ +#ifndef SIMDJSON_SRC_GENERIC_JSON_CHARACTER_BLOCK_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_SRC_GENERIC_JSON_CHARACTER_BLOCK_H */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace lasx { +namespace { + +struct json_character_block { + static simdjson_inline json_character_block classify(const simd::simd8x64& in); + + simdjson_inline uint64_t whitespace() const noexcept { return _whitespace; } + simdjson_inline uint64_t op() const noexcept { return _op; } + simdjson_inline uint64_t scalar() const noexcept { return ~(op() | whitespace()); } + + uint64_t _whitespace; + uint64_t _op; +}; + +} // unnamed namespace +} // namespace lasx +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_JSON_CHARACTER_BLOCK_H +/* end file generic/json_character_block.h for lasx */ +/* end file generic/amalgamated.h for lasx */ +/* including generic/stage1/amalgamated.h for lasx: #include */ +/* begin file generic/stage1/amalgamated.h for lasx */ +// Stuff other things depend on +/* including generic/stage1/base.h for lasx: #include */ +/* begin file generic/stage1/base.h for lasx */ +#ifndef SIMDJSON_SRC_GENERIC_STAGE1_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_SRC_GENERIC_STAGE1_BASE_H */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace lasx { +namespace { +namespace stage1 { + +class bit_indexer; +template +struct buf_block_reader; +struct json_block; +class json_minifier; +class json_scanner; +struct json_string_block; +class json_string_scanner; +class json_structural_indexer; + +} // namespace stage1 + +namespace utf8_validation { +struct utf8_checker; +} // namespace utf8_validation + +using utf8_validation::utf8_checker; + +} // unnamed namespace +} // namespace lasx +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_BASE_H +/* end file generic/stage1/base.h for lasx */ +/* including generic/stage1/buf_block_reader.h for lasx: #include */ +/* begin file generic/stage1/buf_block_reader.h for lasx */ +#ifndef SIMDJSON_SRC_GENERIC_STAGE1_BUF_BLOCK_READER_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_SRC_GENERIC_STAGE1_BUF_BLOCK_READER_H */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include + +namespace simdjson { +namespace lasx { +namespace { +namespace stage1 { + +// Walks through a buffer in block-sized increments, loading the last part with spaces +template +struct buf_block_reader { +public: + simdjson_inline buf_block_reader(const uint8_t *_buf, size_t _len); + simdjson_inline size_t block_index(); + simdjson_inline bool has_full_block() const; + simdjson_inline const uint8_t *full_block() const; + /** + * Get the last block, padded with spaces. + * + * There will always be a last block, with at least 1 byte, unless len == 0 (in which case this + * function fills the buffer with spaces and returns 0. In particular, if len == STEP_SIZE there + * will be 0 full_blocks and 1 remainder block with STEP_SIZE bytes and no spaces for padding. + * + * @return the number of effective characters in the last block. + */ + simdjson_inline size_t get_remainder(uint8_t *dst) const; + simdjson_inline void advance(); +private: + const uint8_t *buf; + const size_t len; + const size_t lenminusstep; + size_t idx; +}; + +// Routines to print masks and text for debugging bitmask operations +simdjson_unused static char * format_input_text_64(const uint8_t *text) { + static char buf[sizeof(simd8x64) + 1]; + for (size_t i=0; i); i++) { + buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]); + } + buf[sizeof(simd8x64)] = '\0'; + return buf; +} + +// Routines to print masks and text for debugging bitmask operations +simdjson_unused static char * format_input_text(const simd8x64& in) { + static char buf[sizeof(simd8x64) + 1]; + in.store(reinterpret_cast(buf)); + for (size_t i=0; i); i++) { + if (buf[i] < ' ') { buf[i] = '_'; } + } + buf[sizeof(simd8x64)] = '\0'; + return buf; +} + +simdjson_unused static char * format_input_text(const simd8x64& in, uint64_t mask) { + static char buf[sizeof(simd8x64) + 1]; + in.store(reinterpret_cast(buf)); + for (size_t i=0; i); i++) { + if (buf[i] <= ' ') { buf[i] = '_'; } + if (!(mask & (size_t(1) << i))) { buf[i] = ' '; } + } + buf[sizeof(simd8x64)] = '\0'; + return buf; +} + +simdjson_unused static char * format_mask(uint64_t mask) { + static char buf[sizeof(simd8x64) + 1]; + for (size_t i=0; i<64; i++) { + buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' '; + } + buf[64] = '\0'; + return buf; +} + +template +simdjson_inline buf_block_reader::buf_block_reader(const uint8_t *_buf, size_t _len) : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE}, idx{0} {} + +template +simdjson_inline size_t buf_block_reader::block_index() { return idx; } + +template +simdjson_inline bool buf_block_reader::has_full_block() const { + return idx < lenminusstep; +} + +template +simdjson_inline const uint8_t *buf_block_reader::full_block() const { + return &buf[idx]; +} + +template +simdjson_inline size_t buf_block_reader::get_remainder(uint8_t *dst) const { + if(len == idx) { return 0; } // memcpy(dst, null, 0) will trigger an error with some sanitizers + std::memset(dst, 0x20, STEP_SIZE); // std::memset STEP_SIZE because it's more efficient to write out 8 or 16 bytes at once. + std::memcpy(dst, buf + idx, len - idx); + return len - idx; +} + +template +simdjson_inline void buf_block_reader::advance() { + idx += STEP_SIZE; +} + +} // namespace stage1 +} // unnamed namespace +} // namespace lasx +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_BUF_BLOCK_READER_H +/* end file generic/stage1/buf_block_reader.h for lasx */ +/* including generic/stage1/json_escape_scanner.h for lasx: #include */ +/* begin file generic/stage1/json_escape_scanner.h for lasx */ +#ifndef SIMDJSON_SRC_GENERIC_STAGE1_JSON_ESCAPE_SCANNER_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_SRC_GENERIC_STAGE1_JSON_ESCAPE_SCANNER_H */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace lasx { +namespace { +namespace stage1 { + +/** + * Scans for escape characters in JSON, taking care with multiple backslashes (\\n vs. \n). + */ +struct json_escape_scanner { + /** The actual escape characters (the backslashes themselves). */ + uint64_t next_is_escaped = 0ULL; + + struct escaped_and_escape { + /** + * Mask of escaped characters. + * + * ``` + * \n \\n \\\n \\\\n \ + * 0100100010100101000 + * n \ \ n \ \ + * ``` + */ + uint64_t escaped; + /** + * Mask of escape characters. + * + * ``` + * \n \\n \\\n \\\\n \ + * 1001000101001010001 + * \ \ \ \ \ \ \ + * ``` + */ + uint64_t escape; + }; + + /** + * Get a mask of both escape and escaped characters (the characters following a backslash). + * + * @param potential_escape A mask of the character that can escape others (but could be + * escaped itself). e.g. block.eq('\\') + */ + simdjson_really_inline escaped_and_escape next(uint64_t backslash) noexcept { + +#if !SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT + if (!backslash) { return {next_escaped_without_backslashes(), 0}; } +#endif + + // | | Mask (shows characters instead of 1's) | Depth | Instructions | + // |--------------------------------|----------------------------------------|-------|---------------------| + // | string | `\\n_\\\n___\\\n___\\\\___\\\\__\\\` | | | + // | | ` even odd even odd odd` | | | + // | potential_escape | ` \ \\\ \\\ \\\\ \\\\ \\\` | 1 | 1 (backslash & ~first_is_escaped) + // | escape_and_terminal_code | ` \n \ \n \ \n \ \ \ \ \ \` | 5 | 5 (next_escape_and_terminal_code()) + // | escaped | `\ \ n \ n \ \ \ \ \ ` X | 6 | 7 (escape_and_terminal_code ^ (potential_escape | first_is_escaped)) + // | escape | ` \ \ \ \ \ \ \ \ \ \` | 6 | 8 (escape_and_terminal_code & backslash) + // | first_is_escaped | `\ ` | 7 (*) | 9 (escape >> 63) () + // (*) this is not needed until the next iteration + uint64_t escape_and_terminal_code = next_escape_and_terminal_code(backslash & ~this->next_is_escaped); + uint64_t escaped = escape_and_terminal_code ^ (backslash | this->next_is_escaped); + uint64_t escape = escape_and_terminal_code & backslash; + this->next_is_escaped = escape >> 63; + return {escaped, escape}; + } + +private: + static constexpr const uint64_t ODD_BITS = 0xAAAAAAAAAAAAAAAAULL; + + simdjson_really_inline uint64_t next_escaped_without_backslashes() noexcept { + uint64_t escaped = this->next_is_escaped; + this->next_is_escaped = 0; + return escaped; + } + + /** + * Returns a mask of the next escape characters (masking out escaped backslashes), along with + * any non-backslash escape codes. + * + * \n \\n \\\n \\\\n returns: + * \n \ \ \n \ \ + * 11 100 1011 10100 + * + * You are expected to mask out the first bit yourself if the previous block had a trailing + * escape. + * + * & the result with potential_escape to get just the escape characters. + * ^ the result with (potential_escape | first_is_escaped) to get escaped characters. + */ + static simdjson_really_inline uint64_t next_escape_and_terminal_code(uint64_t potential_escape) noexcept { + // If we were to just shift and mask out any odd bits, we'd actually get a *half* right answer: + // any even-aligned backslash runs would be correct! Odd-aligned backslash runs would be + // inverted (\\\ would be 010 instead of 101). + // + // ``` + // string: | ____\\\\_\\\\_____ | + // maybe_escaped | ODD | \ \ \ \ | + // even-aligned ^^^ ^^^^ odd-aligned + // ``` + // + // Taking that into account, our basic strategy is: + // + // 1. Use subtraction to produce a mask with 1's for even-aligned runs and 0's for + // odd-aligned runs. + // 2. XOR all odd bits, which masks out the odd bits in even-aligned runs, and brings IN the + // odd bits in odd-aligned runs. + // 3. & with backslash to clean up any stray bits. + // runs are set to 0, and then XORing with "odd": + // + // | | Mask (shows characters instead of 1's) | Instructions | + // |--------------------------------|----------------------------------------|---------------------| + // | string | `\\n_\\\n___\\\n___\\\\___\\\\__\\\` | + // | | ` even odd even odd odd` | + // | maybe_escaped | ` n \\n \\n \\\_ \\\_ \\` X | 1 (potential_escape << 1) + // | maybe_escaped_and_odd | ` \n_ \\n _ \\\n_ _ \\\__ _\\\_ \\\` | 1 (maybe_escaped | odd) + // | even_series_codes_and_odd | ` n_\\\ _ n_ _\\\\ _ _ ` | 1 (maybe_escaped_and_odd - potential_escape) + // | escape_and_terminal_code | ` \n \ \n \ \n \ \ \ \ \ \` | 1 (^ odd) + // + + // Escaped characters are characters following an escape. + uint64_t maybe_escaped = potential_escape << 1; + + // To distinguish odd from even escape sequences, therefore, we turn on any *starting* + // escapes that are on an odd byte. (We actually bring in all odd bits, for speed.) + // - Odd runs of backslashes are 0000, and the code at the end ("n" in \n or \\n) is 1. + // - Odd runs of backslashes are 1111, and the code at the end ("n" in \n or \\n) is 0. + // - All other odd bytes are 1, and even bytes are 0. + uint64_t maybe_escaped_and_odd_bits = maybe_escaped | ODD_BITS; + uint64_t even_series_codes_and_odd_bits = maybe_escaped_and_odd_bits - potential_escape; + + // Now we flip all odd bytes back with xor. This: + // - Makes odd runs of backslashes go from 0000 to 1010 + // - Makes even runs of backslashes go from 1111 to 1010 + // - Sets actually-escaped codes to 1 (the n in \n and \\n: \n = 11, \\n = 100) + // - Resets all other bytes to 0 + return even_series_codes_and_odd_bits ^ ODD_BITS; + } +}; + +} // namespace stage1 +} // unnamed namespace +} // namespace lasx +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRING_SCANNER_H +/* end file generic/stage1/json_escape_scanner.h for lasx */ +/* including generic/stage1/json_string_scanner.h for lasx: #include */ +/* begin file generic/stage1/json_string_scanner.h for lasx */ +#ifndef SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRING_SCANNER_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRING_SCANNER_H */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace lasx { +namespace { +namespace stage1 { + +struct json_string_block { + // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 + simdjson_really_inline json_string_block(uint64_t escaped, uint64_t quote, uint64_t in_string) : + _escaped(escaped), _quote(quote), _in_string(in_string) {} + + // Escaped characters (characters following an escape() character) + simdjson_really_inline uint64_t escaped() const { return _escaped; } + // Real (non-backslashed) quotes + simdjson_really_inline uint64_t quote() const { return _quote; } + // Only characters inside the string (not including the quotes) + simdjson_really_inline uint64_t string_content() const { return _in_string & ~_quote; } + // Return a mask of whether the given characters are inside a string (only works on non-quotes) + simdjson_really_inline uint64_t non_quote_inside_string(uint64_t mask) const { return mask & _in_string; } + // Return a mask of whether the given characters are inside a string (only works on non-quotes) + simdjson_really_inline uint64_t non_quote_outside_string(uint64_t mask) const { return mask & ~_in_string; } + // Tail of string (everything except the start quote) + simdjson_really_inline uint64_t string_tail() const { return _in_string ^ _quote; } + + // escaped characters (backslashed--does not include the hex characters after \u) + uint64_t _escaped; + // real quotes (non-escaped ones) + uint64_t _quote; + // string characters (includes start quote but not end quote) + uint64_t _in_string; +}; + +// Scans blocks for string characters, storing the state necessary to do so +class json_string_scanner { +public: + simdjson_really_inline json_string_block next(const simd::simd8x64& in); + // Returns either UNCLOSED_STRING or SUCCESS + simdjson_really_inline error_code finish(); + +private: + // Scans for escape characters + json_escape_scanner escape_scanner{}; + // Whether the last iteration was still inside a string (all 1's = true, all 0's = false). + uint64_t prev_in_string = 0ULL; +}; + +// +// Return a mask of all string characters plus end quotes. +// +// prev_escaped is overflow saying whether the next character is escaped. +// prev_in_string is overflow saying whether we're still in a string. +// +// Backslash sequences outside of quotes will be detected in stage 2. +// +simdjson_really_inline json_string_block json_string_scanner::next(const simd::simd8x64& in) { + const uint64_t backslash = in.eq('\\'); + const uint64_t escaped = escape_scanner.next(backslash).escaped; + const uint64_t quote = in.eq('"') & ~escaped; + + // + // prefix_xor flips on bits inside the string (and flips off the end quote). + // + // Then we xor with prev_in_string: if we were in a string already, its effect is flipped + // (characters inside strings are outside, and characters outside strings are inside). + // + const uint64_t in_string = prefix_xor(quote) ^ prev_in_string; + + // + // Check if we're still in a string at the end of the box so the next block will know + // + prev_in_string = uint64_t(static_cast(in_string) >> 63); + + // Use ^ to turn the beginning quote off, and the end quote on. + + // We are returning a function-local object so either we get a move constructor + // or we get copy elision. + return json_string_block(escaped, quote, in_string); +} + +simdjson_really_inline error_code json_string_scanner::finish() { + if (prev_in_string) { + return UNCLOSED_STRING; + } + return SUCCESS; +} + +} // namespace stage1 +} // unnamed namespace +} // namespace lasx +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRING_SCANNER_H +/* end file generic/stage1/json_string_scanner.h for lasx */ +/* including generic/stage1/utf8_lookup4_algorithm.h for lasx: #include */ +/* begin file generic/stage1/utf8_lookup4_algorithm.h for lasx */ +#ifndef SIMDJSON_SRC_GENERIC_STAGE1_UTF8_LOOKUP4_ALGORITHM_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_SRC_GENERIC_STAGE1_UTF8_LOOKUP4_ALGORITHM_H */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace lasx { +namespace { +namespace utf8_validation { + +using namespace simd; + + simdjson_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { +// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) +// Bit 1 = Too Long (ASCII followed by continuation) +// Bit 2 = Overlong 3-byte +// Bit 4 = Surrogate +// Bit 5 = Overlong 2-byte +// Bit 7 = Two Continuations + constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______ + // 11______ 11______ + constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______ + constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____ + constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____ + constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______ + constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______ + constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____ + // 11110100 101_____ + // 11110101 1001____ + // 11110101 101_____ + // 1111011_ 1001____ + // 1111011_ 101_____ + // 11111___ 1001____ + // 11111___ 101_____ + constexpr const uint8_t TOO_LARGE_1000 = 1<<6; + // 11110101 1000____ + // 1111011_ 1000____ + // 11111___ 1000____ + constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____ + + const simd8 byte_1_high = prev1.shr<4>().lookup_16( + // 0_______ ________ + TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, + TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, + // 10______ ________ + TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, + // 1100____ ________ + TOO_SHORT | OVERLONG_2, + // 1101____ ________ + TOO_SHORT, + // 1110____ ________ + TOO_SHORT | OVERLONG_3 | SURROGATE, + // 1111____ ________ + TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 + ); + constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . + const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( + // ____0000 ________ + CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, + // ____0001 ________ + CARRY | OVERLONG_2, + // ____001_ ________ + CARRY, + CARRY, + + // ____0100 ________ + CARRY | TOO_LARGE, + // ____0101 ________ + CARRY | TOO_LARGE | TOO_LARGE_1000, + // ____011_ ________ + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + + // ____1___ ________ + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + // ____1101 ________ + CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000 + ); + const simd8 byte_2_high = input.shr<4>().lookup_16( + // ________ 0_______ + TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, + TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, + + // ________ 1000____ + TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, + // ________ 1001____ + TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, + // ________ 101_____ + TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, + TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, + + // ________ 11______ + TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT + ); + return (byte_1_high & byte_1_low & byte_2_high); + } + simdjson_inline simd8 check_multibyte_lengths(const simd8 input, + const simd8 prev_input, const simd8 sc) { + simd8 prev2 = input.prev<2>(prev_input); + simd8 prev3 = input.prev<3>(prev_input); + simd8 must23 = must_be_2_3_continuation(prev2, prev3); + simd8 must23_80 = must23 & uint8_t(0x80); + return must23_80 ^ sc; + } + + // + // Return nonzero if there are incomplete multibyte characters at the end of the block: + // e.g. if there is a 4-byte character, but it's 3 bytes from the end. + // + simdjson_inline simd8 is_incomplete(const simd8 input) { + // If the previous input's last 3 bytes match this, they're too short (they ended at EOF): + // ... 1111____ 111_____ 11______ +#if SIMDJSON_IMPLEMENTATION_ICELAKE || (SIMDJSON_IMPLEMENTATION_RVV_VLS && __riscv_v_fixed_vlen >= 512) + static const uint8_t max_array[64] = { + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0xf0u-1, 0xe0u-1, 0xc0u-1 + }; +#else + static const uint8_t max_array[32] = { + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0xf0u-1, 0xe0u-1, 0xc0u-1 + }; +#endif + const simd8 max_value(&max_array[sizeof(max_array)-sizeof(simd8)]); + return input.gt_bits(max_value); + } + + struct utf8_checker { + // If this is nonzero, there has been a UTF-8 error. + simd8 error; + // The last input we received + simd8 prev_input_block; + // Whether the last input we received was incomplete (used for ASCII fast path) + simd8 prev_incomplete; + + // + // Check whether the current bytes are valid UTF-8. + // + simdjson_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { + // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes + // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) + simd8 prev1 = input.prev<1>(prev_input); + simd8 sc = check_special_cases(input, prev1); + this->error |= check_multibyte_lengths(input, prev_input, sc); + } + + // The only problem that can happen at EOF is that a multibyte character is too short + // or a byte value too large in the last bytes: check_special_cases only checks for bytes + // too large in the first of two bytes. + simdjson_inline void check_eof() { + // If the previous block had incomplete UTF-8 characters at the end, an ASCII block can't + // possibly finish them. + this->error |= this->prev_incomplete; + } + + simdjson_inline void check_next_input(const simd8x64& input) { + if(simdjson_likely(is_ascii(input))) { + this->error |= this->prev_incomplete; + } else { + // you might think that a for-loop would work, but under Visual Studio, it is not good enough. + static_assert((simd8x64::NUM_CHUNKS == 1) + ||(simd8x64::NUM_CHUNKS == 2) + || (simd8x64::NUM_CHUNKS == 4), + "We support one, two or four chunks per 64-byte block."); + SIMDJSON_IF_CONSTEXPR (simd8x64::NUM_CHUNKS == 1) { + this->check_utf8_bytes(input.get<0>(), this->prev_input_block); + } else SIMDJSON_IF_CONSTEXPR (simd8x64::NUM_CHUNKS == 2) { + this->check_utf8_bytes(input.get<0>(), this->prev_input_block); + this->check_utf8_bytes(input.get<1>(), input.get<0>()); + } else SIMDJSON_IF_CONSTEXPR (simd8x64::NUM_CHUNKS == 4) { + this->check_utf8_bytes(input.get<0>(), this->prev_input_block); + this->check_utf8_bytes(input.get<1>(), input.get<0>()); + this->check_utf8_bytes(input.get<2>(), input.get<1>()); + this->check_utf8_bytes(input.get<3>(), input.get<2>()); + } + this->prev_incomplete = is_incomplete(input.get::NUM_CHUNKS-1>()); + this->prev_input_block = input.get::NUM_CHUNKS-1>(); + } + } + // do not forget to call check_eof! + simdjson_warn_unused simdjson_inline error_code errors() { + return this->error.any_bits_set_anywhere() ? error_code::UTF8_ERROR : error_code::SUCCESS; + } + + }; // struct utf8_checker +} // namespace utf8_validation + +} // unnamed namespace +} // namespace lasx +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_UTF8_LOOKUP4_ALGORITHM_H +/* end file generic/stage1/utf8_lookup4_algorithm.h for lasx */ +/* including generic/stage1/json_scanner.h for lasx: #include */ +/* begin file generic/stage1/json_scanner.h for lasx */ +#ifndef SIMDJSON_SRC_GENERIC_STAGE1_JSON_SCANNER_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_SRC_GENERIC_STAGE1_JSON_SCANNER_H */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace lasx { +namespace { +namespace stage1 { + +/** + * A block of scanned json, with information on operators and scalars. + * + * We seek to identify pseudo-structural characters. Anything that is inside + * a string must be omitted (hence & ~_string.string_tail()). + * Otherwise, pseudo-structural characters come in two forms. + * 1. We have the structural characters ([,],{,},:, comma). The + * term 'structural character' is from the JSON RFC. + * 2. We have the 'scalar pseudo-structural characters'. + * Scalars are quotes, and any character except structural characters and white space. + * + * To identify the scalar pseudo-structural characters, we must look at what comes + * before them: it must be a space, a quote or a structural characters. + * Starting with simdjson v0.3, we identify them by + * negation: we identify everything that is followed by a non-quote scalar, + * and we negate that. Whatever remains must be a 'scalar pseudo-structural character'. + */ +struct json_block { +public: + // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 + simdjson_inline json_block(json_string_block&& string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : + _string(std::move(string)), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} + simdjson_inline json_block(json_string_block string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : + _string(string), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} + + /** + * The start of structurals. + * In simdjson prior to v0.3, these were called the pseudo-structural characters. + **/ + simdjson_inline uint64_t structural_start() const noexcept { return potential_structural_start() & ~_string.string_tail(); } + /** All JSON whitespace (i.e. not in a string) */ + simdjson_inline uint64_t whitespace() const noexcept { return non_quote_outside_string(_characters.whitespace()); } + + // Helpers + + /** Whether the given characters are inside a string (only works on non-quotes) */ + simdjson_inline uint64_t non_quote_inside_string(uint64_t mask) const noexcept { return _string.non_quote_inside_string(mask); } + /** Whether the given characters are outside a string (only works on non-quotes) */ + simdjson_inline uint64_t non_quote_outside_string(uint64_t mask) const noexcept { return _string.non_quote_outside_string(mask); } + + // string and escape characters + json_string_block _string; + // whitespace, structural characters ('operators'), scalars + json_character_block _characters; + // whether the previous character was a scalar + uint64_t _follows_potential_nonquote_scalar; +private: + // Potential structurals (i.e. disregarding strings) + + /** + * structural elements ([,],{,},:, comma) plus scalar starts like 123, true and "abc". + * They may reside inside a string. + **/ + simdjson_inline uint64_t potential_structural_start() const noexcept { return _characters.op() | potential_scalar_start(); } + /** + * The start of non-operator runs, like 123, true and "abc". + * It main reside inside a string. + **/ + simdjson_inline uint64_t potential_scalar_start() const noexcept { + // The term "scalar" refers to anything except structural characters and white space + // (so letters, numbers, quotes). + // Whenever it is preceded by something that is not a structural element ({,},[,],:, ") nor a white-space + // then we know that it is irrelevant structurally. + return _characters.scalar() & ~follows_potential_scalar(); + } + /** + * Whether the given character is immediately after a non-operator like 123, true. + * The characters following a quote are not included. + */ + simdjson_inline uint64_t follows_potential_scalar() const noexcept { + // _follows_potential_nonquote_scalar: is defined as marking any character that follows a character + // that is not a structural element ({,},[,],:, comma) nor a quote (") and that is not a + // white space. + // It is understood that within quoted region, anything at all could be marked (irrelevant). + return _follows_potential_nonquote_scalar; + } +}; + +/** + * Scans JSON for important bits: structural characters or 'operators', strings, and scalars. + * + * The scanner starts by calculating two distinct things: + * - string characters (taking \" into account) + * - structural characters or 'operators' ([]{},:, comma) + * and scalars (runs of non-operators like 123, true and "abc") + * + * To minimize data dependency (a key component of the scanner's speed), it finds these in parallel: + * in particular, the operator/scalar bit will find plenty of things that are actually part of + * strings. When we're done, json_block will fuse the two together by masking out tokens that are + * part of a string. + */ +class json_scanner { +public: + json_scanner() = default; + simdjson_inline json_block next(const simd::simd8x64& in); + // Returns either UNCLOSED_STRING or SUCCESS + simdjson_warn_unused simdjson_inline error_code finish(); + +private: + // Whether the last character of the previous iteration is part of a scalar token + // (anything except whitespace or a structural character/'operator'). + uint64_t prev_scalar = 0ULL; + json_string_scanner string_scanner{}; +}; + + +// +// Check if the current character immediately follows a matching character. +// +// For example, this checks for quotes with backslashes in front of them: +// +// const uint64_t backslashed_quote = in.eq('"') & immediately_follows(in.eq('\'), prev_backslash); +// +simdjson_inline uint64_t follows(const uint64_t match, uint64_t &overflow) { + const uint64_t result = match << 1 | overflow; + overflow = match >> 63; + return result; +} + +simdjson_inline json_block json_scanner::next(const simd::simd8x64& in) { + json_string_block strings = string_scanner.next(in); + // identifies the white-space and the structural characters + json_character_block characters = json_character_block::classify(in); + // The term "scalar" refers to anything except structural characters and white space + // (so letters, numbers, quotes). + // We want follows_scalar to mark anything that follows a non-quote scalar (so letters and numbers). + // + // A terminal quote should either be followed by a structural character (comma, brace, bracket, colon) + // or nothing. However, we still want ' "a string"true ' to mark the 't' of 'true' as a potential + // pseudo-structural character just like we would if we had ' "a string" true '; otherwise we + // may need to add an extra check when parsing strings. + // + // Performance: there are many ways to skin this cat. + const uint64_t nonquote_scalar = characters.scalar() & ~strings.quote(); + uint64_t follows_nonquote_scalar = follows(nonquote_scalar, prev_scalar); + // We are returning a function-local object so either we get a move constructor + // or we get copy elision. + return json_block( + strings,// strings is a function-local object so either it moves or the copy is elided. + characters, + follows_nonquote_scalar + ); +} + +simdjson_warn_unused simdjson_inline error_code json_scanner::finish() { + return string_scanner.finish(); +} + +} // namespace stage1 +} // unnamed namespace +} // namespace lasx +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_SCANNER_H +/* end file generic/stage1/json_scanner.h for lasx */ + +// All other declarations +/* including generic/stage1/find_next_document_index.h for lasx: #include */ +/* begin file generic/stage1/find_next_document_index.h for lasx */ +#ifndef SIMDJSON_SRC_GENERIC_STAGE1_FIND_NEXT_DOCUMENT_INDEX_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_SRC_GENERIC_STAGE1_FIND_NEXT_DOCUMENT_INDEX_H */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace lasx { +namespace { +namespace stage1 { + +/** + * This algorithm is used to quickly identify the last structural position that + * makes up a complete document. + * + * It does this by going backwards and finding the last *document boundary* (a + * place where one value follows another without a comma between them). If the + * last document (the characters after the boundary) has an equal number of + * start and end brackets, it is considered complete. + * + * Simply put, we iterate over the structural characters, starting from + * the end. We consider that we found the end of a JSON document when the + * first element of the pair is NOT one of these characters: '{' '[' ':' ',' + * and when the second element is NOT one of these characters: '}' ']' ':' ','. + * + * This simple comparison works most of the time, but it does not cover cases + * where the batch's structural indexes contain a perfect amount of documents. + * In such a case, we do not have access to the structural index which follows + * the last document, therefore, we do not have access to the second element in + * the pair, and that means we cannot identify the last document. To fix this + * issue, we keep a count of the open and closed curly/square braces we found + * while searching for the pair. When we find a pair AND the count of open and + * closed curly/square braces is the same, we know that we just passed a + * complete document, therefore the last json buffer location is the end of the + * batch. + */ +simdjson_inline uint32_t find_next_document_index(dom_parser_implementation &parser) { + // Variant: do not count separately, just figure out depth + if(parser.n_structural_indexes == 0) { return 0; } + auto arr_cnt = 0; + auto obj_cnt = 0; + for (auto i = parser.n_structural_indexes - 1; i > 0; i--) { + auto idxb = parser.structural_indexes[i]; + switch (parser.buf[idxb]) { + case ':': + case ',': + continue; + case '}': + obj_cnt--; + continue; + case ']': + arr_cnt--; + continue; + case '{': + obj_cnt++; + break; + case '[': + arr_cnt++; + break; + } + auto idxa = parser.structural_indexes[i - 1]; + switch (parser.buf[idxa]) { + case '{': + case '[': + case ':': + case ',': + continue; + } + // Last document is complete, so the next document will appear after! + if (!arr_cnt && !obj_cnt) { + return parser.n_structural_indexes; + } + // Last document is incomplete; mark the document at i + 1 as the next one + return i; + } + // If we made it to the end, we want to finish counting to see if we have a full document. + switch (parser.buf[parser.structural_indexes[0]]) { + case '}': + obj_cnt--; + break; + case ']': + arr_cnt--; + break; + case '{': + obj_cnt++; + break; + case '[': + arr_cnt++; + break; + } + if (!arr_cnt && !obj_cnt) { + // We have a complete document. + return parser.n_structural_indexes; + } + return 0; +} + +} // namespace stage1 +} // unnamed namespace +} // namespace lasx +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_FIND_NEXT_DOCUMENT_INDEX_H +/* end file generic/stage1/find_next_document_index.h for lasx */ +/* including generic/stage1/json_minifier.h for lasx: #include */ +/* begin file generic/stage1/json_minifier.h for lasx */ +#ifndef SIMDJSON_SRC_GENERIC_STAGE1_JSON_MINIFIER_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_SRC_GENERIC_STAGE1_JSON_MINIFIER_H */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +// This file contains the common code every implementation uses in stage1 +// It is intended to be included multiple times and compiled multiple times +// We assume the file in which it is included already includes +// "simdjson/stage1.h" (this simplifies amalgation) + +namespace simdjson { +namespace lasx { +namespace { +namespace stage1 { + +class json_minifier { +public: + template + static error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) noexcept; + +private: + simdjson_inline json_minifier(uint8_t *_dst) + : dst{_dst} + {} + template + simdjson_inline void step(const uint8_t *block_buf, buf_block_reader &reader) noexcept; + simdjson_inline void next(const simd::simd8x64& in, const json_block& block); + simdjson_warn_unused simdjson_inline error_code finish(uint8_t *dst_start, size_t &dst_len); + json_scanner scanner{}; + uint8_t *dst; +}; + +simdjson_inline void json_minifier::next(const simd::simd8x64& in, const json_block& block) { + uint64_t mask = block.whitespace(); + dst += in.compress(mask, dst); +} + +simdjson_warn_unused simdjson_inline error_code json_minifier::finish(uint8_t *dst_start, size_t &dst_len) { + error_code error = scanner.finish(); + if (error) { dst_len = 0; return error; } + dst_len = dst - dst_start; + return SUCCESS; +} + +template<> +simdjson_inline void json_minifier::step<128>(const uint8_t *block_buf, buf_block_reader<128> &reader) noexcept { + simd::simd8x64 in_1(block_buf); + simd::simd8x64 in_2(block_buf+64); + json_block block_1 = scanner.next(in_1); + json_block block_2 = scanner.next(in_2); + this->next(in_1, block_1); + this->next(in_2, block_2); + reader.advance(); +} + +template<> +simdjson_inline void json_minifier::step<64>(const uint8_t *block_buf, buf_block_reader<64> &reader) noexcept { + simd::simd8x64 in_1(block_buf); + json_block block_1 = scanner.next(in_1); + this->next(block_buf, block_1); + reader.advance(); +} + +template +error_code json_minifier::minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) noexcept { + buf_block_reader reader(buf, len); + json_minifier minifier(dst); + + // Index the first n-1 blocks + while (reader.has_full_block()) { + minifier.step(reader.full_block(), reader); + } + + // Index the last (remainder) block, padded with spaces + uint8_t block[STEP_SIZE]; + size_t remaining_bytes = reader.get_remainder(block); + if (remaining_bytes > 0) { + // We do not want to write directly to the output stream. Rather, we write + // to a local buffer (for safety). + uint8_t out_block[STEP_SIZE]; + uint8_t * const guarded_dst{minifier.dst}; + minifier.dst = out_block; + minifier.step(block, reader); + size_t to_write = minifier.dst - out_block; + // In some cases, we could be enticed to consider the padded spaces + // as part of the string. This is fine as long as we do not write more + // than we consumed. + if(to_write > remaining_bytes) { to_write = remaining_bytes; } + memcpy(guarded_dst, out_block, to_write); + minifier.dst = guarded_dst + to_write; + } + return minifier.finish(dst, dst_len); +} + +} // namespace stage1 +} // unnamed namespace +} // namespace lasx +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_MINIFIER_H +/* end file generic/stage1/json_minifier.h for lasx */ +/* including generic/stage1/json_structural_indexer.h for lasx: #include */ +/* begin file generic/stage1/json_structural_indexer.h for lasx */ +#ifndef SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRUCTURAL_INDEXER_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRUCTURAL_INDEXER_H */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +// This file contains the common code every implementation uses in stage1 +// It is intended to be included multiple times and compiled multiple times +// We assume the file in which it is included already includes +// "simdjson/stage1.h" (this simplifies amalgation) + +namespace simdjson { +namespace lasx { +namespace { +namespace stage1 { + +class bit_indexer { +public: + uint32_t *tail; + + simdjson_inline bit_indexer(uint32_t *index_buf) : tail(index_buf) {} + +#if SIMDJSON_PREFER_REVERSE_BITS + /** + * ARM lacks a fast trailing zero instruction, but it has a fast + * bit reversal instruction and a fast leading zero instruction. + * Thus it may be profitable to reverse the bits (once) and then + * to rely on a sequence of instructions that call the leading + * zero instruction. + * + * Performance notes: + * The chosen routine is not optimal in terms of data dependency + * since zero_leading_bit might require two instructions. However, + * it tends to minimize the total number of instructions which is + * beneficial. + */ + simdjson_inline void write_index(uint32_t idx, uint64_t& rev_bits, int i) { + int lz = leading_zeroes(rev_bits); + this->tail[i] = static_cast(idx) + lz; + rev_bits = zero_leading_bit(rev_bits, lz); + } +#else + /** + * Under recent x64 systems, we often have both a fast trailing zero + * instruction and a fast 'clear-lower-bit' instruction so the following + * algorithm can be competitive. + */ + + simdjson_inline void write_index(uint32_t idx, uint64_t& bits, int i) { + this->tail[i] = idx + trailing_zeroes(bits); + bits = clear_lowest_bit(bits); + } +#endif // SIMDJSON_PREFER_REVERSE_BITS + + template + simdjson_inline int write_indexes(uint32_t idx, uint64_t& bits) { + write_index(idx, bits, START); + SIMDJSON_IF_CONSTEXPR (N > 1) { + write_indexes<(N-1>0?START+1:START), (N-1>=0?N-1:1)>(idx, bits); + } + return START+N; + } + + template + simdjson_inline int write_indexes_stepped(uint32_t idx, uint64_t& bits, int cnt) { + write_indexes(idx, bits); + SIMDJSON_IF_CONSTEXPR ((START+STEP) < END) { + if (simdjson_unlikely((START+STEP) < cnt)) { + write_indexes_stepped<(START+STEP(idx, bits, cnt); + } + } + return ((END-START) % STEP) == 0 ? END : (END-START) - ((END-START) % STEP) + STEP; + } + + // flatten out values in 'bits' assuming that they are are to have values of idx + // plus their position in the bitvector, and store these indexes at + // base_ptr[base] incrementing base as we go + // will potentially store extra values beyond end of valid bits, so base_ptr + // needs to be large enough to handle this + // + // If the kernel sets SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER, then it + // will provide its own version of the code. +#ifdef SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER + simdjson_inline void write(uint32_t idx, uint64_t bits); +#else + simdjson_inline void write(uint32_t idx, uint64_t bits) { + // In some instances, the next branch is expensive because it is mispredicted. + // Unfortunately, in other cases, + // it helps tremendously. + if (bits == 0) + return; + + int cnt = static_cast(count_ones(bits)); + +#if SIMDJSON_PREFER_REVERSE_BITS + bits = reverse_bits(bits); +#endif +#ifdef SIMDJSON_STRUCTURAL_INDEXER_STEP + static constexpr const int STEP = SIMDJSON_STRUCTURAL_INDEXER_STEP; +#else + static constexpr const int STEP = 4; +#endif + static constexpr const int STEP_UNTIL = 24; + + write_indexes_stepped<0, STEP_UNTIL, STEP>(idx, bits, cnt); + SIMDJSON_IF_CONSTEXPR (STEP_UNTIL < 64) { + if (simdjson_unlikely(STEP_UNTIL < cnt)) { + for (int i=STEP_UNTIL; itail += cnt; + } +#endif // SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER + +}; + +class json_structural_indexer { +public: + /** + * Find the important bits of JSON in a 128-byte chunk, and add them to structural_indexes. + * + * @param partial Setting the partial parameter to true allows the find_structural_bits to + * tolerate unclosed strings. The caller should still ensure that the input is valid UTF-8. If + * you are processing substrings, you may want to call on a function like trimmed_length_safe_utf8. + */ + template + static error_code index(const uint8_t *buf, size_t len, dom_parser_implementation &parser, stage1_mode partial) noexcept; + +private: + simdjson_inline json_structural_indexer(uint32_t *structural_indexes); + template + simdjson_inline void step(const uint8_t *block, buf_block_reader &reader) noexcept; + simdjson_inline void next(const simd::simd8x64& in, const json_block& block, size_t idx); + simdjson_warn_unused simdjson_inline error_code finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial); + + json_scanner scanner{}; + utf8_checker checker{}; + bit_indexer indexer; + uint64_t prev_structurals = 0; + uint64_t unescaped_chars_error = 0; +}; + +simdjson_inline json_structural_indexer::json_structural_indexer(uint32_t *structural_indexes) : indexer{structural_indexes} {} + +// Skip the last character if it is partial +simdjson_inline size_t trim_partial_utf8(const uint8_t *buf, size_t len) { + if (simdjson_unlikely(len < 3)) { + switch (len) { + case 2: + if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left + if (buf[len-2] >= 0xe0) { return len-2; } // 3- and 4-byte characters with only 2 bytes left + return len; + case 1: + if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left + return len; + case 0: + return len; + } + } + if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left + if (buf[len-2] >= 0xe0) { return len-2; } // 3- and 4-byte characters with only 1 byte left + if (buf[len-3] >= 0xf0) { return len-3; } // 4-byte characters with only 3 bytes left + return len; +} + +// +// PERF NOTES: +// We pipe 2 inputs through these stages: +// 1. Load JSON into registers. This takes a long time and is highly parallelizable, so we load +// 2 inputs' worth at once so that by the time step 2 is looking for them input, it's available. +// 2. Scan the JSON for critical data: strings, scalars and operators. This is the critical path. +// The output of step 1 depends entirely on this information. These functions don't quite use +// up enough CPU: the second half of the functions is highly serial, only using 1 execution core +// at a time. The second input's scans has some dependency on the first ones finishing it, but +// they can make a lot of progress before they need that information. +// 3. Step 1 does not use enough capacity, so we run some extra stuff while we're waiting for that +// to finish: utf-8 checks and generating the output from the last iteration. +// +// The reason we run 2 inputs at a time, is steps 2 and 3 are *still* not enough to soak up all +// available capacity with just one input. Running 2 at a time seems to give the CPU a good enough +// workout. +// +template +error_code json_structural_indexer::index(const uint8_t *buf, size_t len, dom_parser_implementation &parser, stage1_mode partial) noexcept { + if (simdjson_unlikely(len > parser.capacity())) { return CAPACITY; } + // We guard the rest of the code so that we can assume that len > 0 throughout. + if (len == 0) { return EMPTY; } + if (is_streaming(partial)) { + len = trim_partial_utf8(buf, len); + // If you end up with an empty window after trimming + // the partial UTF-8 bytes, then chances are good that you + // have an UTF-8 formatting error. + if(len == 0) { return UTF8_ERROR; } + } + buf_block_reader reader(buf, len); + json_structural_indexer indexer(parser.structural_indexes.get()); + + // Read all but the last block + while (reader.has_full_block()) { + indexer.step(reader.full_block(), reader); + } + // Take care of the last block (will always be there unless file is empty which is + // not supposed to happen.) + uint8_t block[STEP_SIZE]; + if (simdjson_unlikely(reader.get_remainder(block) == 0)) { return UNEXPECTED_ERROR; } + indexer.step(block, reader); + return indexer.finish(parser, reader.block_index(), len, partial); +} + +template<> +simdjson_inline void json_structural_indexer::step<128>(const uint8_t *block, buf_block_reader<128> &reader) noexcept { + simd::simd8x64 in_1(block); + simd::simd8x64 in_2(block+64); + json_block block_1 = scanner.next(in_1); + json_block block_2 = scanner.next(in_2); + this->next(in_1, block_1, reader.block_index()); + this->next(in_2, block_2, reader.block_index()+64); + reader.advance(); +} + +template<> +simdjson_inline void json_structural_indexer::step<64>(const uint8_t *block, buf_block_reader<64> &reader) noexcept { + simd::simd8x64 in_1(block); + json_block block_1 = scanner.next(in_1); + this->next(in_1, block_1, reader.block_index()); + reader.advance(); +} + +simdjson_inline void json_structural_indexer::next(const simd::simd8x64& in, const json_block& block, size_t idx) { + uint64_t unescaped = in.lteq(0x1F); +#if SIMDJSON_UTF8VALIDATION + checker.check_next_input(in); +#endif + indexer.write(uint32_t(idx-64), prev_structurals); // Output *last* iteration's structurals to the parser + prev_structurals = block.structural_start(); + unescaped_chars_error |= block.non_quote_inside_string(unescaped); +} + +simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial) { + // Write out the final iteration's structurals + indexer.write(uint32_t(idx-64), prev_structurals); + error_code error = scanner.finish(); + // We deliberately break down the next expression so that it is + // human readable. + const bool should_we_exit = is_streaming(partial) ? + ((error != SUCCESS) && (error != UNCLOSED_STRING)) // when partial we tolerate UNCLOSED_STRING + : (error != SUCCESS); // if partial is false, we must have SUCCESS + const bool have_unclosed_string = (error == UNCLOSED_STRING); + if (simdjson_unlikely(should_we_exit)) { return error; } + + if (unescaped_chars_error) { + return UNESCAPED_CHARS; + } + parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get()); + /*** + * The On-Demand API requires special padding. + * + * This is related to https://github.com/simdjson/simdjson/issues/906 + * Basically, we want to make sure that if the parsing continues beyond the last (valid) + * structural character, it quickly stops. + * Only three structural characters can be repeated without triggering an error in JSON: [,] and }. + * We repeat the padding character (at 'len'). We don't know what it is, but if the parsing + * continues, then it must be [,] or }. + * Suppose it is ] or }. We backtrack to the first character, what could it be that would + * not trigger an error? It could be ] or } but no, because you can't start a document that way. + * It can't be a comma, a colon or any simple value. So the only way we could continue is + * if the repeated character is [. But if so, the document must start with [. But if the document + * starts with [, it should end with ]. If we enforce that rule, then we would get + * ][[ which is invalid. + * + * This is illustrated with the test array_iterate_unclosed_error() on the following input: + * R"({ "a": [,,)" + **/ + parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); // used later in partial == stage1_mode::streaming_final + parser.structural_indexes[parser.n_structural_indexes + 1] = uint32_t(len); + parser.structural_indexes[parser.n_structural_indexes + 2] = 0; + parser.next_structural_index = 0; + // a valid JSON file cannot have zero structural indexes - we should have found something + if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { + return EMPTY; + } + if (simdjson_unlikely(parser.structural_indexes[parser.n_structural_indexes - 1] > len)) { + return UNEXPECTED_ERROR; + } + if (partial == stage1_mode::streaming_partial) { + // If we have an unclosed string, then the last structural + // will be the quote and we want to make sure to omit it. + if(have_unclosed_string) { + parser.n_structural_indexes--; + // a valid JSON file cannot have zero structural indexes - we should have found something + if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; } + } + // We truncate the input to the end of the last complete document (or zero). + auto new_structural_indexes = find_next_document_index(parser); + if (new_structural_indexes == 0 && parser.n_structural_indexes > 0) { + if(parser.structural_indexes[0] == 0) { + // If the buffer is partial and we started at index 0 but the document is + // incomplete, it's too big to parse. + return CAPACITY; + } else { + // It is possible that the document could be parsed, we just had a lot + // of white space. + parser.n_structural_indexes = 0; + return EMPTY; + } + } + + parser.n_structural_indexes = new_structural_indexes; + } else if (partial == stage1_mode::streaming_final) { + if(have_unclosed_string) { parser.n_structural_indexes--; } + // We truncate the input to the end of the last complete document (or zero). + // Because partial == stage1_mode::streaming_final, it means that we may + // silently ignore trailing garbage. Though it sounds bad, we do it + // deliberately because many people who have streams of JSON documents + // will truncate them for processing. E.g., imagine that you are uncompressing + // the data from a size file or receiving it in chunks from the network. You + // may not know where exactly the last document will be. Meanwhile the + // document_stream instances allow people to know the JSON documents they are + // parsing (see the iterator.source() method). + parser.n_structural_indexes = find_next_document_index(parser); + // We store the initial n_structural_indexes so that the client can see + // whether we used truncation. If initial_n_structural_indexes == parser.n_structural_indexes, + // then this will query parser.structural_indexes[parser.n_structural_indexes] which is len, + // otherwise, it will copy some prior index. + parser.structural_indexes[parser.n_structural_indexes + 1] = parser.structural_indexes[parser.n_structural_indexes]; + // This next line is critical, do not change it unless you understand what you are + // doing. + parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); + if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { + // We tolerate an unclosed string at the very end of the stream. Indeed, users + // often load their data in bulk without being careful and they want us to ignore + // the trailing garbage. + return EMPTY; + } + } + checker.check_eof(); + return checker.errors(); +} + +} // namespace stage1 +} // unnamed namespace +} // namespace lasx +} // namespace simdjson + +// Clear CUSTOM_BIT_INDEXER so other implementations can set it if they need to. +#undef SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRUCTURAL_INDEXER_H +/* end file generic/stage1/json_structural_indexer.h for lasx */ +/* including generic/stage1/utf8_validator.h for lasx: #include */ +/* begin file generic/stage1/utf8_validator.h for lasx */ +#ifndef SIMDJSON_SRC_GENERIC_STAGE1_UTF8_VALIDATOR_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_SRC_GENERIC_STAGE1_UTF8_VALIDATOR_H */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace lasx { +namespace { +namespace stage1 { + +/** + * Validates that the string is actual UTF-8. + */ +template +bool generic_validate_utf8(const uint8_t * input, size_t length) { + checker c{}; + buf_block_reader<64> reader(input, length); + while (reader.has_full_block()) { + simd::simd8x64 in(reader.full_block()); + c.check_next_input(in); + reader.advance(); + } + uint8_t block[64]{}; + reader.get_remainder(block); + simd::simd8x64 in(block); + c.check_next_input(in); + reader.advance(); + c.check_eof(); + return c.errors() == error_code::SUCCESS; +} + +bool generic_validate_utf8(const char * input, size_t length) { + return generic_validate_utf8(reinterpret_cast(input),length); +} + +} // namespace stage1 +} // unnamed namespace +} // namespace lasx +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE1_UTF8_VALIDATOR_H +/* end file generic/stage1/utf8_validator.h for lasx */ +/* end file generic/stage1/amalgamated.h for lasx */ +/* including generic/stage2/amalgamated.h for lasx: #include */ +/* begin file generic/stage2/amalgamated.h for lasx */ +// Stuff other things depend on +/* including generic/stage2/base.h for lasx: #include */ +/* begin file generic/stage2/base.h for lasx */ +#ifndef SIMDJSON_SRC_GENERIC_STAGE2_BASE_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_SRC_GENERIC_STAGE2_BASE_H */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace lasx { +namespace { +namespace stage2 { + +class json_iterator; +class structural_iterator; +struct tape_builder; +struct tape_writer; + +} // namespace stage2 +} // unnamed namespace +} // namespace lasx +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE2_BASE_H +/* end file generic/stage2/base.h for lasx */ +/* including generic/stage2/tape_writer.h for lasx: #include */ +/* begin file generic/stage2/tape_writer.h for lasx */ +#ifndef SIMDJSON_SRC_GENERIC_STAGE2_TAPE_WRITER_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_SRC_GENERIC_STAGE2_TAPE_WRITER_H */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include + +namespace simdjson { +namespace lasx { +namespace { +namespace stage2 { + +struct tape_writer { + /** The next place to write to tape */ + uint64_t *next_tape_loc; + + /** Write a signed 64-bit value to tape. */ + simdjson_inline void append_s64(int64_t value) noexcept; + + /** Write an unsigned 64-bit value to tape. */ + simdjson_inline void append_u64(uint64_t value) noexcept; + + /** Write a double value to tape. */ + simdjson_inline void append_double(double value) noexcept; + + /** + * Append a tape entry (an 8-bit type,and 56 bits worth of value). + */ + simdjson_inline void append(uint64_t val, internal::tape_type t) noexcept; + + /** + * Skip the current tape entry without writing. + * + * Used to skip the start of the container, since we'll come back later to fill it in when the + * container ends. + */ + simdjson_inline void skip() noexcept; + + /** + * Skip the number of tape entries necessary to write a large u64 or i64. + */ + simdjson_inline void skip_large_integer() noexcept; + + /** + * Skip the number of tape entries necessary to write a double. + */ + simdjson_inline void skip_double() noexcept; + + /** + * Write a value to a known location on tape. + * + * Used to go back and write out the start of a container after the container ends. + */ + simdjson_inline static void write(uint64_t &tape_loc, uint64_t val, internal::tape_type t) noexcept; + +private: + /** + * Append both the tape entry, and a supplementary value following it. Used for types that need + * all 64 bits, such as double and uint64_t. + */ + template + simdjson_inline void append2(uint64_t val, T val2, internal::tape_type t) noexcept; +}; // struct tape_writer + +simdjson_inline void tape_writer::append_s64(int64_t value) noexcept { + append2(0, value, internal::tape_type::INT64); +} + +simdjson_inline void tape_writer::append_u64(uint64_t value) noexcept { + append(0, internal::tape_type::UINT64); + *next_tape_loc = value; + next_tape_loc++; +} + +/** Write a double value to tape. */ +simdjson_inline void tape_writer::append_double(double value) noexcept { + append2(0, value, internal::tape_type::DOUBLE); +} + +simdjson_inline void tape_writer::skip() noexcept { + next_tape_loc++; +} + +simdjson_inline void tape_writer::skip_large_integer() noexcept { + next_tape_loc += 2; +} + +simdjson_inline void tape_writer::skip_double() noexcept { + next_tape_loc += 2; +} + +simdjson_inline void tape_writer::append(uint64_t val, internal::tape_type t) noexcept { + *next_tape_loc = val | ((uint64_t(char(t))) << 56); + next_tape_loc++; +} + +template +simdjson_inline void tape_writer::append2(uint64_t val, T val2, internal::tape_type t) noexcept { + append(val, t); + static_assert(sizeof(val2) == sizeof(*next_tape_loc), "Type is not 64 bits!"); + memcpy(next_tape_loc, &val2, sizeof(val2)); + next_tape_loc++; +} + +simdjson_inline void tape_writer::write(uint64_t &tape_loc, uint64_t val, internal::tape_type t) noexcept { + tape_loc = val | ((uint64_t(char(t))) << 56); +} + +} // namespace stage2 +} // unnamed namespace +} // namespace lasx +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_WRITER_H +/* end file generic/stage2/tape_writer.h for lasx */ +/* including generic/stage2/logger.h for lasx: #include */ +/* begin file generic/stage2/logger.h for lasx */ +#ifndef SIMDJSON_SRC_GENERIC_STAGE2_LOGGER_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_SRC_GENERIC_STAGE2_LOGGER_H */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include + + +// This is for an internal-only stage 2 specific logger. +// Set LOG_ENABLED = true to log what stage 2 is doing! +namespace simdjson { +namespace lasx { +namespace { +namespace logger { + + static constexpr const char * DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"; + +#if SIMDJSON_VERBOSE_LOGGING + static constexpr const bool LOG_ENABLED = true; +#else + static constexpr const bool LOG_ENABLED = false; +#endif + 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 constexpr const int LOG_INDEX_LEN = 5; + + static int log_depth; // Not threadsafe. Log only. + + // Helper to turn unprintable or newline characters into spaces + static simdjson_inline char printable_char(char c) { + if (c >= 0x20) { + return c; + } else { + return ' '; + } + } + + // Print the header and set up log_start + static simdjson_inline void log_start() { + if (LOG_ENABLED) { + log_depth = 0; + printf("\n"); + printf("| %-*s | %-*s | %-*s | %-*s | Detail |\n", LOG_EVENT_LEN, "Event", LOG_BUFFER_LEN, "Buffer", LOG_SMALL_BUFFER_LEN, "Next", 5, "Next#"); + printf("|%.*s|%.*s|%.*s|%.*s|--------|\n", LOG_EVENT_LEN+2, DASHES, LOG_BUFFER_LEN+2, DASHES, LOG_SMALL_BUFFER_LEN+2, DASHES, 5+2, DASHES); + } + } + + simdjson_unused static simdjson_inline void log_string(const char *message) { + if (LOG_ENABLED) { + printf("%s\n", message); + } + } + + // Logs a single line from the stage 2 DOM parser + template + static simdjson_inline void log_line(S &structurals, const char *title_prefix, const char *title, const char *detail) { + if (LOG_ENABLED) { + printf("| %*s%s%-*s ", log_depth*2, "", title_prefix, LOG_EVENT_LEN - log_depth*2 - int(strlen(title_prefix)), title); + auto current_index = structurals.at_beginning() ? nullptr : structurals.next_structural-1; + auto next_index = structurals.next_structural; + auto current = current_index ? &structurals.buf[*current_index] : reinterpret_cast(" "); + auto next = &structurals.buf[*next_index]; + { + // Print the next N characters in the buffer. + printf("| "); + // Otherwise, print the characters starting from the buffer position. + // Print spaces for unprintable or newline characters. + for (int i=0;i */ +/* begin file generic/stage2/json_iterator.h for lasx */ +#ifndef SIMDJSON_SRC_GENERIC_STAGE2_JSON_ITERATOR_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_SRC_GENERIC_STAGE2_JSON_ITERATOR_H */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace lasx { +namespace { +namespace stage2 { + +class json_iterator { +public: + const uint8_t* const buf; + uint32_t *next_structural; + dom_parser_implementation &dom_parser; + uint32_t depth{0}; + + /** + * Walk the JSON document. + * + * The visitor receives callbacks when values are encountered. All callbacks pass the iterator as + * the first parameter; some callbacks have other parameters as well: + * + * - visit_document_start() - at the beginning. + * - visit_document_end() - at the end (if things were successful). + * + * - visit_array_start() - at the start `[` of a non-empty array. + * - visit_array_end() - at the end `]` of a non-empty array. + * - visit_empty_array() - when an empty array is encountered. + * + * - visit_object_end() - at the start `]` of a non-empty object. + * - visit_object_start() - at the end `]` of a non-empty object. + * - visit_empty_object() - when an empty object is encountered. + * - visit_key(const uint8_t *key) - when a key in an object field is encountered. key is + * guaranteed to point at the first quote of the string (`"key"`). + * - visit_primitive(const uint8_t *value) - when a value is a string, number, boolean or null. + * - visit_root_primitive(iter, uint8_t *value) - when the top-level value is a string, number, boolean or null. + * + * - increment_count(iter) - each time a value is found in an array or object. + */ + template + simdjson_warn_unused simdjson_inline error_code walk_document(V &visitor) noexcept; + + /** + * Create an iterator capable of walking a JSON document. + * + * The document must have already passed through stage 1. + */ + simdjson_inline json_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index); + + /** + * Look at the next token. + * + * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). + * + * They may include invalid JSON as well (such as `1.2.3` or `ture`). + */ + simdjson_inline const uint8_t *peek() const noexcept; + /** + * Advance to the next token. + * + * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). + * + * They may include invalid JSON as well (such as `1.2.3` or `ture`). + */ + simdjson_inline const uint8_t *advance() noexcept; + /** + * Get the remaining length of the document, from the start of the current token. + */ + simdjson_inline size_t remaining_len() const noexcept; + /** + * Check if we are at the end of the document. + * + * If this is true, there are no more tokens. + */ + simdjson_inline bool at_eof() const noexcept; + /** + * Check if we are at the beginning of the document. + */ + simdjson_inline bool at_beginning() const noexcept; + simdjson_inline uint8_t last_structural() const noexcept; + + /** + * Log that a value has been found. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_value(const char *type) const noexcept; + /** + * Log the start of a multipart value. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_start_value(const char *type) const noexcept; + /** + * Log the end of a multipart value. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_end_value(const char *type) const noexcept; + /** + * Log an error. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_error(const char *error) const noexcept; + + template + simdjson_warn_unused simdjson_inline error_code visit_root_primitive(V &visitor, const uint8_t *value) noexcept; + template + simdjson_warn_unused simdjson_inline error_code visit_primitive(V &visitor, const uint8_t *value) noexcept; +}; + +template +simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &visitor) noexcept { + logger::log_start(); + + // + // Start the document + // + if (at_eof()) { return EMPTY; } + log_start_value("document"); + SIMDJSON_TRY( visitor.visit_document_start(*this) ); + + // + // Read first value + // + { + auto value = advance(); + + // Make sure the outer object or array is closed before continuing; otherwise, there are ways we + // could get into memory corruption. See https://github.com/simdjson/simdjson/issues/906 + if (!STREAMING) { + switch (*value) { + case '{': if (last_structural() != '}') { log_value("starting brace unmatched"); return TAPE_ERROR; }; break; + case '[': if (last_structural() != ']') { log_value("starting bracket unmatched"); return TAPE_ERROR; }; break; + } + } + + switch (*value) { + case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; + case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; + default: SIMDJSON_TRY( visitor.visit_root_primitive(*this, value) ); break; + } + } + goto document_end; + +// +// Object parser states +// +object_begin: + log_start_value("object"); + depth++; + if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } + dom_parser.is_array[depth] = false; + SIMDJSON_TRY( visitor.visit_object_start(*this) ); + + { + auto key = advance(); + if (*key != '"') { log_error("Object does not start with a key"); return TAPE_ERROR; } + SIMDJSON_TRY( visitor.increment_count(*this) ); + SIMDJSON_TRY( visitor.visit_key(*this, key) ); + } + +object_field: + if (simdjson_unlikely( *advance() != ':' )) { log_error("Missing colon after key in object"); return TAPE_ERROR; } + { + auto value = advance(); + switch (*value) { + case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; + case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; + default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break; + } + } + +object_continue: + switch (*advance()) { + case ',': + SIMDJSON_TRY( visitor.increment_count(*this) ); + { + auto key = advance(); + if (simdjson_unlikely( *key != '"' )) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; } + SIMDJSON_TRY( visitor.visit_key(*this, key) ); + } + goto object_field; + case '}': log_end_value("object"); SIMDJSON_TRY( visitor.visit_object_end(*this) ); goto scope_end; + default: log_error("No comma between object fields"); return TAPE_ERROR; + } + +scope_end: + depth--; + if (depth == 0) { goto document_end; } + if (dom_parser.is_array[depth]) { goto array_continue; } + goto object_continue; + +// +// Array parser states +// +array_begin: + log_start_value("array"); + depth++; + if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } + dom_parser.is_array[depth] = true; + SIMDJSON_TRY( visitor.visit_array_start(*this) ); + SIMDJSON_TRY( visitor.increment_count(*this) ); + +array_value: + { + auto value = advance(); + switch (*value) { + case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; + case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; + default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break; + } + } + +array_continue: + switch (*advance()) { + case ',': SIMDJSON_TRY( visitor.increment_count(*this) ); goto array_value; + case ']': log_end_value("array"); SIMDJSON_TRY( visitor.visit_array_end(*this) ); goto scope_end; + default: log_error("Missing comma between array values"); return TAPE_ERROR; + } + +document_end: + log_end_value("document"); + SIMDJSON_TRY( visitor.visit_document_end(*this) ); + + dom_parser.next_structural_index = uint32_t(next_structural - &dom_parser.structural_indexes[0]); + + // If we didn't make it to the end, it's an error + if ( !STREAMING && dom_parser.next_structural_index != dom_parser.n_structural_indexes ) { + log_error("More than one JSON value at the root of the document, or extra characters at the end of the JSON!"); + return TAPE_ERROR; + } + + return SUCCESS; + +} // walk_document() + +simdjson_inline json_iterator::json_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index) + : buf{_dom_parser.buf}, + next_structural{&_dom_parser.structural_indexes[start_structural_index]}, + dom_parser{_dom_parser} { +} + +simdjson_inline const uint8_t *json_iterator::peek() const noexcept { + return &buf[*(next_structural)]; +} +simdjson_inline const uint8_t *json_iterator::advance() noexcept { + return &buf[*(next_structural++)]; +} +simdjson_inline size_t json_iterator::remaining_len() const noexcept { + return dom_parser.len - *(next_structural-1); +} + +simdjson_inline bool json_iterator::at_eof() const noexcept { + return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; +} +simdjson_inline bool json_iterator::at_beginning() const noexcept { + return next_structural == dom_parser.structural_indexes.get(); +} +simdjson_inline uint8_t json_iterator::last_structural() const noexcept { + return buf[dom_parser.structural_indexes[dom_parser.n_structural_indexes - 1]]; +} + +simdjson_inline void json_iterator::log_value(const char *type) const noexcept { + logger::log_line(*this, "", type, ""); +} + +simdjson_inline void json_iterator::log_start_value(const char *type) const noexcept { + logger::log_line(*this, "+", type, ""); + if (logger::LOG_ENABLED) { logger::log_depth++; } +} + +simdjson_inline void json_iterator::log_end_value(const char *type) const noexcept { + if (logger::LOG_ENABLED) { logger::log_depth--; } + logger::log_line(*this, "-", type, ""); +} + +simdjson_inline void json_iterator::log_error(const char *error) const noexcept { + logger::log_line(*this, "", "ERROR", error); +} + +template +simdjson_warn_unused simdjson_inline error_code json_iterator::visit_root_primitive(V &visitor, const uint8_t *value) noexcept { + switch (*value) { + case '"': return visitor.visit_root_string(*this, value); + case 't': return visitor.visit_root_true_atom(*this, value); + case 'f': return visitor.visit_root_false_atom(*this, value); + case 'n': return visitor.visit_root_null_atom(*this, value); + case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return visitor.visit_root_number(*this, value); + default: + log_error("Document starts with a non-value character"); + return TAPE_ERROR; + } +} +template +simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V &visitor, const uint8_t *value) noexcept { + // Use the fact that most scalars are going to be either strings or numbers. + if(*value == '"') { + return visitor.visit_string(*this, value); + } else if (((*value - '0') < 10) || (*value == '-')) { + return visitor.visit_number(*this, value); + } + // true, false, null are uncommon. + switch (*value) { + case 't': return visitor.visit_true_atom(*this, value); + case 'f': return visitor.visit_false_atom(*this, value); + case 'n': return visitor.visit_null_atom(*this, value); + default: + log_error("Non-value found when value was expected!"); + return TAPE_ERROR; + } +} + +} // namespace stage2 +} // unnamed namespace +} // namespace lasx +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE2_JSON_ITERATOR_H +/* end file generic/stage2/json_iterator.h for lasx */ +/* including generic/stage2/stringparsing.h for lasx: #include */ +/* begin file generic/stage2/stringparsing.h for lasx */ +#include +#ifndef SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +// This file contains the common code every implementation uses +// It is intended to be included multiple times and compiled multiple times + +namespace simdjson { +namespace lasx { +namespace { +/// @private +namespace stringparsing { + +// begin copypasta +// These chars yield themselves: " \ / +// b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab +// u not handled in this table as it's complex +static const uint8_t escape_map[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0. + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4. + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5. + 0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6. + 0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7. + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +// handle a unicode codepoint +// write appropriate values into dest +// src will advance 6 bytes or 12 bytes +// dest will advance a variable amount (return via pointer) +// return true if the unicode codepoint was valid +// We work in little-endian then swap at write time +simdjson_warn_unused +simdjson_inline bool handle_unicode_codepoint(const uint8_t **src_ptr, + uint8_t **dst_ptr, bool allow_replacement) { + // Use the default Unicode Character 'REPLACEMENT CHARACTER' (U+FFFD) + constexpr uint32_t substitution_code_point = 0xfffd; + // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the + // conversion is not valid; we defer the check for this to inside the + // multilingual plane check. + uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); + *src_ptr += 6; + + // If we found a high surrogate, we must + // check for low surrogate for characters + // outside the Basic + // Multilingual Plane. + if (code_point >= 0xd800 && code_point < 0xdc00) { + const uint8_t *src_data = *src_ptr; + /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ + if (((src_data[0] << 8) | src_data[1]) != ((static_cast ('\\') << 8) | static_cast ('u'))) { + if(!allow_replacement) { return false; } + code_point = substitution_code_point; + } else { + uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); + + // We have already checked that the high surrogate is valid and + // (code_point - 0xd800) < 1024. + // + // Check that code_point_2 is in the range 0xdc00..0xdfff + // and that code_point_2 was parsed from valid hex. + uint32_t low_bit = code_point_2 - 0xdc00; + if (low_bit >> 10) { + if(!allow_replacement) { return false; } + code_point = substitution_code_point; + } else { + code_point = (((code_point - 0xd800) << 10) | low_bit) + 0x10000; + *src_ptr += 6; + } + + } + } else if (code_point >= 0xdc00 && code_point <= 0xdfff) { + // If we encounter a low surrogate (not preceded by a high surrogate) + // then we have an error. + if(!allow_replacement) { return false; } + code_point = substitution_code_point; + } + size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); + *dst_ptr += offset; + return offset > 0; +} + + +// handle a unicode codepoint using the wobbly convention +// https://simonsapin.github.io/wtf-8/ +// write appropriate values into dest +// src will advance 6 bytes or 12 bytes +// dest will advance a variable amount (return via pointer) +// return true if the unicode codepoint was valid +// We work in little-endian then swap at write time +simdjson_warn_unused +simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t **src_ptr, + uint8_t **dst_ptr) { + // It is not ideal that this function is nearly identical to handle_unicode_codepoint. + // + // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the + // conversion is not valid; we defer the check for this to inside the + // multilingual plane check. + uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); + *src_ptr += 6; + // If we found a high surrogate, we must + // check for low surrogate for characters + // outside the Basic + // Multilingual Plane. + if (code_point >= 0xd800 && code_point < 0xdc00) { + const uint8_t *src_data = *src_ptr; + /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ + if (((src_data[0] << 8) | src_data[1]) == ((static_cast ('\\') << 8) | static_cast ('u'))) { + uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); + uint32_t low_bit = code_point_2 - 0xdc00; + if ((low_bit >> 10) == 0) { + code_point = + (((code_point - 0xd800) << 10) | low_bit) + 0x10000; + *src_ptr += 6; + } + } + } + + size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); + *dst_ptr += offset; + return offset > 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. + */ +simdjson_warn_unused simdjson_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) { + while (1) { + // Copy the next n bytes, and find the backslash and quote in them. + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); + // If the next thing is the end quote, copy and return + if (bs_quote.has_quote_first()) { + // we encountered quotes first. Move dst to point to quotes and exit + return dst + bs_quote.quote_index(); + } + if (bs_quote.has_backslash()) { + /* find out where the backspace is */ + auto bs_dist = bs_quote.backslash_index(); + uint8_t escape_char = src[bs_dist + 1]; + /* we encountered backslash first. Handle backslash */ + if (escape_char == 'u') { + /* move src/dst up to the start; they will be further adjusted + within the unicode codepoint handling code. */ + src += bs_dist; + dst += bs_dist; + if (!handle_unicode_codepoint(&src, &dst, allow_replacement)) { + return nullptr; + } + } else { + /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and + * write bs_dist+1 characters to output + * note this may reach beyond the part of the buffer we've actually + * seen. I think this is ok */ + uint8_t escape_result = escape_map[escape_char]; + if (escape_result == 0u) { + return nullptr; /* bogus escape value is an error */ + } + dst[bs_dist] = escape_result; + src += bs_dist + 2; + dst += bs_dist + 1; + } + } else { + /* they are the same. Since they can't co-occur, it means we + * encountered neither. */ + src += backslash_and_quote::BYTES_PROCESSED; + dst += backslash_and_quote::BYTES_PROCESSED; + } + } +} + +simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) { + // It is not ideal that this function is nearly identical to parse_string. + while (1) { + // Copy the next n bytes, and find the backslash and quote in them. + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); + // If the next thing is the end quote, copy and return + if (bs_quote.has_quote_first()) { + // we encountered quotes first. Move dst to point to quotes and exit + return dst + bs_quote.quote_index(); + } + if (bs_quote.has_backslash()) { + /* find out where the backspace is */ + auto bs_dist = bs_quote.backslash_index(); + uint8_t escape_char = src[bs_dist + 1]; + /* we encountered backslash first. Handle backslash */ + if (escape_char == 'u') { + /* move src/dst up to the start; they will be further adjusted + within the unicode codepoint handling code. */ + src += bs_dist; + dst += bs_dist; + if (!handle_unicode_codepoint_wobbly(&src, &dst)) { + return nullptr; + } + } else { + /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and + * write bs_dist+1 characters to output + * note this may reach beyond the part of the buffer we've actually + * seen. I think this is ok */ + uint8_t escape_result = escape_map[escape_char]; + if (escape_result == 0u) { + return nullptr; /* bogus escape value is an error */ + } + dst[bs_dist] = escape_result; + src += bs_dist + 2; + dst += bs_dist + 1; + } + } else { + /* they are the same. Since they can't co-occur, it means we + * encountered neither. */ + src += backslash_and_quote::BYTES_PROCESSED; + dst += backslash_and_quote::BYTES_PROCESSED; + } + } +} + +} // namespace stringparsing + +} // unnamed namespace +} // namespace lasx +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H +/* end file generic/stage2/stringparsing.h for lasx */ +/* including generic/stage2/structural_iterator.h for lasx: #include */ +/* begin file generic/stage2/structural_iterator.h for lasx */ +#ifndef SIMDJSON_SRC_GENERIC_STAGE2_STRUCTURAL_ITERATOR_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_SRC_GENERIC_STAGE2_STRUCTURAL_ITERATOR_H */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace lasx { +namespace { +namespace stage2 { + +class structural_iterator { +public: + const uint8_t* const buf; + uint32_t *next_structural; + dom_parser_implementation &dom_parser; + + // Start a structural + simdjson_inline structural_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index) + : buf{_dom_parser.buf}, + next_structural{&_dom_parser.structural_indexes[start_structural_index]}, + dom_parser{_dom_parser} { + } + // Get the buffer position of the current structural character + simdjson_inline const uint8_t* current() { + return &buf[*(next_structural-1)]; + } + // Get the current structural character + simdjson_inline char current_char() { + return buf[*(next_structural-1)]; + } + // Get the next structural character without advancing + simdjson_inline char peek_next_char() { + return buf[*next_structural]; + } + simdjson_inline const uint8_t* peek() { + return &buf[*next_structural]; + } + simdjson_inline const uint8_t* advance() { + return &buf[*(next_structural++)]; + } + simdjson_inline char advance_char() { + return buf[*(next_structural++)]; + } + simdjson_inline size_t remaining_len() { + return dom_parser.len - *(next_structural-1); + } + + simdjson_inline bool at_end() { + return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; + } + simdjson_inline bool at_beginning() { + return next_structural == dom_parser.structural_indexes.get(); + } +}; + +} // namespace stage2 +} // unnamed namespace +} // namespace lasx +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE2_STRUCTURAL_ITERATOR_H +/* end file generic/stage2/structural_iterator.h for lasx */ +/* including generic/stage2/tape_builder.h for lasx: #include */ +/* begin file generic/stage2/tape_builder.h for lasx */ +#ifndef SIMDJSON_SRC_GENERIC_STAGE2_TAPE_BUILDER_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_SRC_GENERIC_STAGE2_TAPE_BUILDER_H */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #include */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + + +namespace simdjson { +namespace lasx { +namespace { +namespace stage2 { + +struct tape_builder { + template + simdjson_warn_unused static simdjson_inline error_code parse_document( + dom_parser_implementation &dom_parser, + dom::document &doc) noexcept; + + /** Called when a non-empty document starts. */ + simdjson_warn_unused simdjson_inline error_code visit_document_start(json_iterator &iter) noexcept; + /** Called when a non-empty document ends without error. */ + simdjson_warn_unused simdjson_inline error_code visit_document_end(json_iterator &iter) noexcept; + + /** Called when a non-empty array starts. */ + simdjson_warn_unused simdjson_inline error_code visit_array_start(json_iterator &iter) noexcept; + /** Called when a non-empty array ends. */ + simdjson_warn_unused simdjson_inline error_code visit_array_end(json_iterator &iter) noexcept; + /** Called when an empty array is found. */ + simdjson_warn_unused simdjson_inline error_code visit_empty_array(json_iterator &iter) noexcept; + + /** Called when a non-empty object starts. */ + simdjson_warn_unused simdjson_inline error_code visit_object_start(json_iterator &iter) noexcept; + /** + * Called when a key in a field is encountered. + * + * primitive, visit_object_start, visit_empty_object, visit_array_start, or visit_empty_array + * will be called after this with the field value. + */ + simdjson_warn_unused simdjson_inline error_code visit_key(json_iterator &iter, const uint8_t *key) noexcept; + /** Called when a non-empty object ends. */ + simdjson_warn_unused simdjson_inline error_code visit_object_end(json_iterator &iter) noexcept; + /** Called when an empty object is found. */ + simdjson_warn_unused simdjson_inline error_code visit_empty_object(json_iterator &iter) noexcept; + + /** + * Called when a string, number, boolean or null is found. + */ + simdjson_warn_unused simdjson_inline error_code visit_primitive(json_iterator &iter, const uint8_t *value) noexcept; + /** + * Called when a string, number, boolean or null is found at the top level of a document (i.e. + * when there is no array or object and the entire document is a single string, number, boolean or + * null. + * + * This is separate from primitive() because simdjson's normal primitive parsing routines assume + * there is at least one more token after the value, which is only true in an array or object. + */ + simdjson_warn_unused simdjson_inline error_code visit_root_primitive(json_iterator &iter, const uint8_t *value) noexcept; + + simdjson_warn_unused simdjson_inline error_code visit_string(json_iterator &iter, const uint8_t *value, bool key = false) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_number(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_true_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_false_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_null_atom(json_iterator &iter, const uint8_t *value) noexcept; + + simdjson_warn_unused simdjson_inline error_code visit_root_string(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_number(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_true_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept; + + /** Called each time a new field or element in an array or object is found. */ + simdjson_warn_unused simdjson_inline error_code increment_count(json_iterator &iter) noexcept; + + /** Next location to write to tape */ + tape_writer tape; +private: + /** Next write location in the string buf for stage 2 parsing */ + uint8_t *current_string_buf_loc; + + simdjson_inline tape_builder(dom::document &doc) noexcept; + + simdjson_inline uint32_t next_tape_index(json_iterator &iter) const noexcept; + simdjson_inline void start_container(json_iterator &iter) noexcept; + simdjson_warn_unused simdjson_inline error_code end_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept; + simdjson_warn_unused simdjson_inline error_code empty_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept; + simdjson_inline uint8_t *on_start_string(json_iterator &iter) noexcept; + simdjson_inline void on_end_string(uint8_t *dst) noexcept; +}; // struct tape_builder + +template +simdjson_warn_unused simdjson_inline error_code tape_builder::parse_document( + dom_parser_implementation &dom_parser, + dom::document &doc) noexcept { + dom_parser.doc = &doc; + json_iterator iter(dom_parser, STREAMING ? dom_parser.next_structural_index : 0); + tape_builder builder(doc); + return iter.walk_document(builder); +} + +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_primitive(json_iterator &iter, const uint8_t *value) noexcept { + return iter.visit_root_primitive(*this, value); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_primitive(json_iterator &iter, const uint8_t *value) noexcept { + return iter.visit_primitive(*this, value); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_object(json_iterator &iter) noexcept { + return empty_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_array(json_iterator &iter) noexcept { + return empty_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); +} + +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_start(json_iterator &iter) noexcept { + start_container(iter); + return SUCCESS; +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_start(json_iterator &iter) noexcept { + start_container(iter); + return SUCCESS; +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_start(json_iterator &iter) noexcept { + start_container(iter); + return SUCCESS; +} + +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_end(json_iterator &iter) noexcept { + return end_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_end(json_iterator &iter) noexcept { + return end_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_end(json_iterator &iter) noexcept { + constexpr uint32_t start_tape_index = 0; + tape.append(start_tape_index, internal::tape_type::ROOT); + tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter), internal::tape_type::ROOT); + return SUCCESS; +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_key(json_iterator &iter, const uint8_t *key) noexcept { + return visit_string(iter, key, true); +} + +simdjson_warn_unused simdjson_inline error_code tape_builder::increment_count(json_iterator &iter) noexcept { + iter.dom_parser.open_containers[iter.depth].count++; // we have a key value pair in the object at parser.dom_parser.depth - 1 + return SUCCESS; +} + +simdjson_inline tape_builder::tape_builder(dom::document &doc) noexcept : tape{doc.tape.get()}, current_string_buf_loc{doc.string_buf.get()} {} + +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_string(json_iterator &iter, const uint8_t *value, bool key) noexcept { + iter.log_value(key ? "key" : "string"); + uint8_t *dst = on_start_string(iter); + dst = stringparsing::parse_string(value+1, dst, false); // We do not allow replacement when the escape characters are invalid. + if (dst == nullptr) { + iter.log_error("Invalid escape in string"); + return STRING_ERROR; + } + on_end_string(dst); + return SUCCESS; +} + +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_string(json_iterator &iter, const uint8_t *value) noexcept { + return visit_string(iter, value); +} + +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_number(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("number"); + return numberparsing::parse_number(value, tape); +} + +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_number(json_iterator &iter, const uint8_t *value) noexcept { + // + // We need to make a copy to make sure that the string is space terminated. + // This is not about padding the input, which should already padded up + // to len + SIMDJSON_PADDING. However, we have no control at this stage + // on how the padding was done. What if the input string was padded with nulls? + // It is quite common for an input string to have an extra null character (C string). + // We do not want to allow 9\0 (where \0 is the null character) inside a JSON + // document, but the string "9\0" by itself is fine. So we make a copy and + // pad the input with spaces when we know that there is just one input element. + // This copy is relatively expensive, but it will almost never be called in + // practice unless you are in the strange scenario where you have many JSON + // documents made of single atoms. + // + std::unique_ptrcopy(new (std::nothrow) uint8_t[iter.remaining_len() + SIMDJSON_PADDING]); + if (copy.get() == nullptr) { return MEMALLOC; } + std::memcpy(copy.get(), value, iter.remaining_len()); + std::memset(copy.get() + iter.remaining_len(), ' ', SIMDJSON_PADDING); + error_code error = visit_number(iter, copy.get()); + return error; +} + +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_true_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("true"); + if (!atomparsing::is_valid_true_atom(value)) { return T_ATOM_ERROR; } + tape.append(0, internal::tape_type::TRUE_VALUE); + return SUCCESS; +} + +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_true_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("true"); + if (!atomparsing::is_valid_true_atom(value, iter.remaining_len())) { return T_ATOM_ERROR; } + tape.append(0, internal::tape_type::TRUE_VALUE); + return SUCCESS; +} + +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_false_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("false"); + if (!atomparsing::is_valid_false_atom(value)) { return F_ATOM_ERROR; } + tape.append(0, internal::tape_type::FALSE_VALUE); + return SUCCESS; +} + +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("false"); + if (!atomparsing::is_valid_false_atom(value, iter.remaining_len())) { return F_ATOM_ERROR; } + tape.append(0, internal::tape_type::FALSE_VALUE); + return SUCCESS; +} + +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_null_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("null"); + if (!atomparsing::is_valid_null_atom(value)) { return N_ATOM_ERROR; } + tape.append(0, internal::tape_type::NULL_VALUE); + return SUCCESS; +} + +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("null"); + if (!atomparsing::is_valid_null_atom(value, iter.remaining_len())) { return N_ATOM_ERROR; } + tape.append(0, internal::tape_type::NULL_VALUE); + return SUCCESS; +} + +// private: + +simdjson_inline uint32_t tape_builder::next_tape_index(json_iterator &iter) const noexcept { + return uint32_t(tape.next_tape_loc - iter.dom_parser.doc->tape.get()); +} + +simdjson_warn_unused simdjson_inline error_code tape_builder::empty_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept { + auto start_index = next_tape_index(iter); + tape.append(start_index+2, start); + tape.append(start_index, end); + return SUCCESS; +} + +simdjson_inline void tape_builder::start_container(json_iterator &iter) noexcept { + iter.dom_parser.open_containers[iter.depth].tape_index = next_tape_index(iter); + iter.dom_parser.open_containers[iter.depth].count = 0; + tape.skip(); // We don't actually *write* the start element until the end. +} + +simdjson_warn_unused simdjson_inline error_code tape_builder::end_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept { + // Write the ending tape element, pointing at the start location + const uint32_t start_tape_index = iter.dom_parser.open_containers[iter.depth].tape_index; + tape.append(start_tape_index, end); + // Write the start tape element, pointing at the end location (and including count) + // count can overflow if it exceeds 24 bits... so we saturate + // the convention being that a cnt of 0xffffff or more is undetermined in value (>= 0xffffff). + const uint32_t count = iter.dom_parser.open_containers[iter.depth].count; + const uint32_t cntsat = count > 0xFFFFFF ? 0xFFFFFF : count; + tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter) | (uint64_t(cntsat) << 32), start); + return SUCCESS; +} + +simdjson_inline uint8_t *tape_builder::on_start_string(json_iterator &iter) noexcept { + // we advance the point, accounting for the fact that we have a NULL termination + tape.append(current_string_buf_loc - iter.dom_parser.doc->string_buf.get(), internal::tape_type::STRING); + return current_string_buf_loc + sizeof(uint32_t); +} + +simdjson_inline void tape_builder::on_end_string(uint8_t *dst) noexcept { + uint32_t str_length = uint32_t(dst - (current_string_buf_loc + sizeof(uint32_t))); + // TODO check for overflow in case someone has a crazy string (>=4GB?) + // But only add the overflow check when the document itself exceeds 4GB + // Currently unneeded because we refuse to parse docs larger or equal to 4GB. + memcpy(current_string_buf_loc, &str_length, sizeof(uint32_t)); + // NULL termination is still handy if you expect all your strings to + // be NULL terminated? It comes at a small cost + *dst = 0; + current_string_buf_loc = dst + 1; +} + +} // namespace stage2 +} // unnamed namespace +} // namespace lasx +} // namespace simdjson + +#endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_BUILDER_H +/* end file generic/stage2/tape_builder.h for lasx */ +/* end file generic/stage2/amalgamated.h for lasx */ + +// +// Stage 1 +// +namespace simdjson { +namespace lasx { + +simdjson_warn_unused error_code implementation::create_dom_parser_implementation( + size_t capacity, + size_t max_depth, + std::unique_ptr& dst +) const noexcept { + dst.reset( new (std::nothrow) dom_parser_implementation() ); + if (!dst) { return MEMALLOC; } + if (auto err = dst->set_capacity(capacity)) + return err; + if (auto err = dst->set_max_depth(max_depth)) + return err; + return SUCCESS; +} + +namespace { + +using namespace simd; + +simdjson_inline json_character_block json_character_block::classify(const simd::simd8x64& in) { + // Inspired by haswell. + // LASX use low 5 bits as index. For the 6 operators (:,[]{}), the unique-5bits is [6:2]. + // The ASCII white-space and operators have these values: (char, hex, unique-5bits) + // (' ', 20, 00000) ('\t', 09, 01001) ('\n', 0A, 01010) ('\r', 0D, 01101) + // (',', 2C, 01011) (':', 3A, 01110) ('[', 5B, 10110) ('{', 7B, 11110) (']', 5D, 10111) ('}', 7D, 11111) + const simd8 ws_table = simd8::repeat_16( + ' ', 0, 0, 0, 0, 0, 0, 0, 0, '\t', '\n', 0, 0, '\r', 0, 0 + ); + const simd8 op_table_lo = simd8::repeat_16( + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ',', 0, 0, ':', 0 + ); + const simd8 op_table_hi = simd8::repeat_16( + 0, 0, 0, 0, 0, 0, '[', ']', 0, 0, 0, 0, 0, 0, '{', '}' + ); + uint64_t ws = in.eq({ + in.chunks[0].lookup_16(ws_table), + in.chunks[1].lookup_16(ws_table), + }); + uint64_t op = in.eq({ + __lasx_xvshuf_b(op_table_hi, op_table_lo, in.chunks[0].shr<2>()), + __lasx_xvshuf_b(op_table_hi, op_table_lo, in.chunks[1].shr<2>()), + }); + + return { ws, op }; +} + +simdjson_inline bool is_ascii(const simd8x64& input) { + return input.reduce_or().is_ascii(); +} + +simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { + simd8 is_third_byte = prev2.saturating_sub(0xe0u-0x80); // Only 111_____ will be >= 0x80 + simd8 is_fourth_byte = prev3.saturating_sub(0xf0u-0x80); // Only 1111____ will be >= 0x80 + return is_third_byte | is_fourth_byte; +} + +} // unnamed namespace +} // namespace lasx +} // namespace simdjson + +// +// Stage 2 +// + +// +// Implementation-specific overrides +// +namespace simdjson { +namespace lasx { + +simdjson_warn_unused error_code implementation::minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept { + return lasx::stage1::json_minifier::minify<64>(buf, len, dst, dst_len); +} + +simdjson_warn_unused error_code dom_parser_implementation::stage1(const uint8_t *_buf, size_t _len, stage1_mode streaming) noexcept { + this->buf = _buf; + this->len = _len; + return lasx::stage1::json_structural_indexer::index<64>(buf, len, *this, streaming); +} + +simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept { + return lasx::stage1::generic_validate_utf8(buf,len); +} + +simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept { + return stage2::tape_builder::parse_document(*this, _doc); +} + +simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept { + return stage2::tape_builder::parse_document(*this, _doc); +} + +SIMDJSON_NO_SANITIZE_MEMORY +simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept { + return lasx::stringparsing::parse_string(src, dst, allow_replacement); +} + +simdjson_warn_unused uint8_t *dom_parser_implementation::parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept { + return lasx::stringparsing::parse_wobbly_string(src, dst); +} + +simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept { + auto error = stage1(_buf, _len, stage1_mode::regular); + if (error) { return error; } + return stage2(_doc); +} + +} // namespace lasx +} // namespace simdjson + +/* including simdjson/lasx/end.h: #include */ +/* 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_SRC_LASX_CPP +/* end file lasx.cpp */ +#endif #if SIMDJSON_IMPLEMENTATION_LSX /* including lsx.cpp: #include */ /* begin file lsx.cpp */ @@ -40268,21 +48224,21 @@ SIMDJSON_UNTARGET_REGION /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - /** - * Implementation for LSX. - */ - namespace lsx { +/** + * Implementation for LSX. + */ +namespace lsx { - class implementation; +class implementation; - namespace { - namespace simd { - template struct simd8; - template struct simd8x64; - } // namespace simd - } // unnamed namespace +namespace { +namespace simd { +template struct simd8; +template struct simd8x64; +} // namespace simd +} // unnamed namespace - } // namespace lsx +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_LSX_BASE_H @@ -40296,8 +48252,6 @@ namespace simdjson { /* amalgamation skipped (editor-only): #include "simdjson/lsx/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 static_assert(sizeof(__m128i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for LoongArch SX"); @@ -40316,43 +48270,43 @@ static_assert(sizeof(__m128i) <= simdjson::SIMDJSON_PADDING, "insufficient paddi /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace { +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); - } +// 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 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 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); - } +/* 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); +} - simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t* result) { - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); - } +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t *result) { + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); +} - } // unnamed namespace - } // namespace lsx +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_LSX_BITMANIPULATION_H @@ -40367,26 +48321,26 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace { +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; - } +// +// 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 +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif @@ -40405,33 +48359,39 @@ namespace simdjson { #include namespace simdjson { - namespace lsx { - namespace numberparsing { +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); - } +// 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; - } +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 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 */ @@ -40447,347 +48407,348 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace { - namespace simd { +namespace lsx { +namespace { +namespace simd { - // Forward-declared so they can be used by splat and friends. - template - struct base { - __m128i value; + // Forward-declared so they can be used by splat and friends. + template + struct base { + __m128i value; - // Zero constructor - simdjson_inline base() : value{ __m128i() } {} + // Zero constructor + simdjson_inline base() : value{__m128i()} {} - // Conversion from SIMD register - simdjson_inline base(const __m128i _value) : value(_value) {} + // 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; } + // 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(this); *this_cast = *this_cast | other; return *this_cast; } - simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } - simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } - }; + // 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(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; - // Forward-declared so they can be used by splat and friends. - template - struct simd8; + // Forward-declared so they can be used by splat and friends. + template + struct simd8; - template> - struct base8 : base> { - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m128i _value) : base>(_value) {} + template> + struct base8: base> { + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m128i _value) : base>(_value) {} - friend simdjson_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return __lsx_vseq_b(lhs, rhs); } + friend simdjson_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return __lsx_vseq_b(lhs, rhs); } - static const int SIZE = sizeof(base>::value); + static const int SIZE = sizeof(base>::value); - template - simdjson_inline simd8 prev(const simd8 prev_chunk) const { - return __lsx_vor_v(__lsx_vbsll_v(*this, N), __lsx_vbsrl_v(prev_chunk, 16 - N)); - } - }; + template + simdjson_inline simd8 prev(const simd8 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 : base8 { - static simdjson_inline simd8 splat(bool _value) { - return __lsx_vreplgr2vr_b(uint8_t(-(!!_value))); - } + // SIMD byte mask type (returned by things like eq and gt) + template<> + struct simd8: base8 { + static simdjson_inline simd8 splat(bool _value) { + return __lsx_vreplgr2vr_b(uint8_t(-(!!_value))); + } - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m128i _value) : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) : base8(splat(_value)) {} + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m128i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(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 operator~() const { return *this ^ true; } - }; + 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 operator~() const { return *this ^ true; } + }; - template - struct base8_numeric : base8 { - static simdjson_inline simd8 splat(T _value) { return __lsx_vreplgr2vr_b(_value); } - static simdjson_inline simd8 zero() { return __lsx_vldi(0); } - static simdjson_inline simd8 load(const T values[16]) { - return __lsx_vld(reinterpret_cast(values), 0); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdjson_inline simd8 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + template + struct base8_numeric: base8 { + static simdjson_inline simd8 splat(T _value) { return __lsx_vreplgr2vr_b(_value); } + static simdjson_inline simd8 zero() { return __lsx_vldi(0); } + static simdjson_inline simd8 load(const T values[16]) { + return __lsx_vld(reinterpret_cast(values), 0); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - simdjson_inline base8_numeric() : base8() {} - simdjson_inline base8_numeric(const __m128i _value) : base8(_value) {} + simdjson_inline base8_numeric() : base8() {} + simdjson_inline base8_numeric(const __m128i _value) : base8(_value) {} - // Store to array - simdjson_inline void store(T dst[16]) const { - return __lsx_vst(*this, reinterpret_cast<__m128i*>(dst), 0); - } + // 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 operator+(const simd8 other) const { return __lsx_vadd_b(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return __lsx_vsub_b(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { return __lsx_vadd_b(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return __lsx_vsub_b(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } - // Override to distinguish from bool version - simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return __lsx_vshuf_b(lookup_table, lookup_table, *this); - } + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 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 - 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(reinterpret_cast(pshufb_combine_table)), pop1 * 8); - __m128i answer = __lsx_vshuf_b(pruned, pruned, compactmask); - __lsx_vst(answer, reinterpret_cast(output), 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. + template + 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(reinterpret_cast(pshufb_combine_table)), pop1 * 8); + __m128i answer = __lsx_vshuf_b(pruned, pruned, compactmask); + __lsx_vst(answer, reinterpret_cast(output), 0); + } - template - simdjson_inline simd8 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::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - }; + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; - // Signed bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + // Signed bytes + template<> + struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - // Order-sensitive comparisons - simdjson_inline simd8 max_val(const simd8 other) const { return __lsx_vmax_b(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return __lsx_vmin_b(*this, other); } - simdjson_inline simd8 operator>(const simd8 other) const { return __lsx_vslt_b(other, *this); } - simdjson_inline simd8 operator<(const simd8 other) const { return __lsx_vslt_b(*this, other); } - }; + // Order-sensitive comparisons + simdjson_inline simd8 max_val(const simd8 other) const { return __lsx_vmax_b(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return __lsx_vmin_b(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return __lsx_vslt_b(other, *this); } + simdjson_inline simd8 operator<(const simd8 other) const { return __lsx_vslt_b(*this, other); } + }; - // Unsigned bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + // Unsigned bytes + template<> + struct simd8: base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - // Saturated math - simdjson_inline simd8 saturating_add(const simd8 other) const { return __lsx_vsadd_bu(*this, other); } - simdjson_inline simd8 saturating_sub(const simd8 other) const { return __lsx_vssub_bu(*this, other); } + // Saturated math + simdjson_inline simd8 saturating_add(const simd8 other) const { return __lsx_vsadd_bu(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return __lsx_vssub_bu(*this, other); } - // Order-specific operations - simdjson_inline simd8 max_val(const simd8 other) const { return __lsx_vmax_bu(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return __lsx_vmin_bu(other, *this); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } - simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } - simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } - simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } + // Order-specific operations + simdjson_inline simd8 max_val(const simd8 other) const { return __lsx_vmax_bu(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return __lsx_vmin_bu(other, *this); } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } + simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } + simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } + simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } - // Bit-specific operations - simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } - simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } - simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } - simdjson_inline simd8 any_bits_set(simd8 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 bits) const { - return 0 == __lsx_vpickve2gr_hu(__lsx_vmsknz_b(__lsx_vand_v(*this, bits)), 0); - } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } - template - simdjson_inline simd8 shr() const { return simd8(__lsx_vsrli_b(*this, N)); } - template - simdjson_inline simd8 shl() const { return simd8(__lsx_vslli_b(*this, N)); } - }; + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } + simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } + simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } + simdjson_inline simd8 any_bits_set(simd8 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 bits) const { + return 0 == __lsx_vpickve2gr_hu(__lsx_vmsknz_b(__lsx_vand_v(*this, bits)), 0); + } + simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } + template + simdjson_inline simd8 shr() const { return simd8(__lsx_vsrli_b(*this, N)); } + template + simdjson_inline simd8 shl() const { return simd8(__lsx_vslli_b(*this, N)); } + }; - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 4, "LSX kernel should use four registers per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 4, "LSX kernel should use four registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{ chunk0, chunk1, chunk2, chunk3 } {} - simdjson_inline simd8x64(const T ptr[64]) : chunks{ simd8::load(ptr), simd8::load(ptr + 16), simd8::load(ptr + 32), simd8::load(ptr + 48) } {} + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::load(ptr+16), simd8::load(ptr+32), simd8::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(output); - // There should be a critical value which processes in scaler is faster. - if (zcnt1) - this->chunks[0].compress(mask1, reinterpret_cast(voutput)); - voutput += zcnt1; - if (zcnt2) - this->chunks[1].compress(mask2, reinterpret_cast(voutput)); - voutput += zcnt2; - if (zcnt3) - this->chunks[2].compress(mask3, reinterpret_cast(voutput)); - voutput += zcnt3; - if (zcnt4) - this->chunks[3].compress(mask4, reinterpret_cast(voutput)); - voutput += zcnt4; - return reinterpret_cast(voutput) - reinterpret_cast(output); - } + 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(output); + // There should be a critical value which processes in scaler is faster. + if (zcnt1) + this->chunks[0].compress(mask1, reinterpret_cast(voutput)); + voutput += zcnt1; + if (zcnt2) + this->chunks[1].compress(mask2, reinterpret_cast(voutput)); + voutput += zcnt2; + if (zcnt3) + this->chunks[2].compress(mask3, reinterpret_cast(voutput)); + voutput += zcnt3; + if (zcnt4) + this->chunks[3].compress(mask4, reinterpret_cast(voutput)); + voutput += zcnt4; + return reinterpret_cast(voutput) - reinterpret_cast(output); + } - simdjson_inline void store(T ptr[64]) const { - this->chunks[0].store(ptr + sizeof(simd8) * 0); - this->chunks[1].store(ptr + sizeof(simd8) * 1); - this->chunks[2].store(ptr + sizeof(simd8) * 2); - this->chunks[3].store(ptr + sizeof(simd8) * 3); - } + simdjson_inline void store(T ptr[64]) const { + this->chunks[0].store(ptr+sizeof(simd8)*0); + this->chunks[1].store(ptr+sizeof(simd8)*1); + this->chunks[2].store(ptr+sizeof(simd8)*2); + this->chunks[3].store(ptr+sizeof(simd8)*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 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 reduce_or() const { - return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]); - } + simdjson_inline simd8 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] == mask, - this->chunks[1] == mask, - this->chunks[2] == mask, - this->chunks[3] == mask - ).to_bitmask(); - } + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + 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& other) const { - return simd8x64( - 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 eq(const simd8x64 &other) const { + return simd8x64( + 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] <= mask, - this->chunks[1] <= mask, - this->chunks[2] <= mask, - this->chunks[3] <= mask - ).to_bitmask(); - } - }; // struct simd8x64 + simdjson_inline uint64_t lteq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] <= mask, + this->chunks[1] <= mask, + this->chunks[2] <= mask, + this->chunks[3] <= mask + ).to_bitmask(); + } + }; // struct simd8x64 - } // namespace simd - } // unnamed namespace - } // namespace lsx +} // namespace simd +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_LSX_SIMD_H @@ -40804,46 +48765,71 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace { +namespace lsx { +namespace { - using namespace simd; +using namespace simd; - // Holds backslashes and quotes locations. - struct backslash_and_quote { - public: - static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t* src, uint8_t* dst); +// 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); } + 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 + 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 v0(src); - simd8 v1(src + sizeof(v0)); - v0.store(dst); - v1.store(dst + sizeof(v0)); +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 v0(src); + simd8 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(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); - return { - uint32_t(bs_and_quote), // bs_bits - uint32_t(bs_and_quote >> 32) // quote_bits - }; - } + // 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(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); + return { + uint32_t(bs_and_quote), // bs_bits + uint32_t(bs_and_quote >> 32) // quote_bits + }; +} - } // unnamed namespace - } // namespace lsx + +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 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + static_cast((is_backslash | is_quote | is_control).to_bitmask()) + }; +} + +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_LSX_STRINGPARSING_DEFS_H @@ -40878,10 +48864,12 @@ namespace simdjson { /* 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_LSX */ -/* amalgamation skipped (editor-only): #include "simdjson/lsx/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 */ @@ -40891,22 +48879,22 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { +namespace lsx { - struct open_container; - class dom_parser_implementation; +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 - }; +/** + * 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 lsx } // namespace simdjson #endif // SIMDJSON_GENERIC_BASE_H @@ -40923,99 +48911,97 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace { - namespace jsoncharutils { +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]; - } +// 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]; - } +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; - } +// 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 - } +// 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; - } +// 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 jsoncharutils +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_GENERIC_JSONCHARUTILS_H @@ -41033,69 +49019,69 @@ namespace simdjson { #include namespace simdjson { - namespace lsx { - namespace { - /// @private - namespace atomparsing { +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; } +// 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; } - // 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); - } +// 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); +} - 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) { + 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_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) { + 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_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) { + 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; } - } +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; } +} - } // namespace atomparsing - } // unnamed namespace - } // namespace lsx +} // namespace atomparsing +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_GENERIC_ATOMPARSING_H @@ -41111,83 +49097,83 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { +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 +// 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"); +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_containers{}; - /** Whether each open container is a [ or { */ - std::unique_ptr 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{}; +class dom_parser_implementation final : public internal::dom_parser_implementation { +public: + /** Tape location of each open { or [ */ + std::unique_ptr open_containers{}; + /** Whether each open container is a [ or { */ + std::unique_ptr 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; + 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); + 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 lsx } // namespace simdjson namespace simdjson { - namespace lsx { +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; +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) { return CAPACITY; } - // Stage 1 index output - size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; - 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; +// 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) { return CAPACITY; } + // Stage 1 index output + size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; + 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; - } + _capacity = capacity; + return SUCCESS; +} - inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { - // 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; } +inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { + // 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; - } + _max_depth = max_depth; + return SUCCESS; +} - } // namespace lsx +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H @@ -41202,129 +49188,148 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { +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 : public internal::implementation_simdjson_result_base { - * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} - * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} - * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} - * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} - * // Your extra methods here - * } - * - * Then any method returning simdjson_result will be chainable with your methods. - */ - template - struct implementation_simdjson_result_base { +// 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 : public internal::implementation_simdjson_result_base { + * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} + * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} + * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} + * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} + * // Your extra methods here + * } + * + * Then any method returning simdjson_result will be chainable with your methods. + */ +template +struct implementation_simdjson_result_base { - /** - * Create a new empty result with error = UNINITIALIZED. - */ - simdjson_inline implementation_simdjson_result_base() noexcept = default; + /** + * 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 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 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; + /** + * 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 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; + /** + * 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_inline error_code error() const 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& value() & noexcept(false); + /** + * 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); - /** - * Take the result value (move it). - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline T&& value() && 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&& take_value() && noexcept(false); + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& 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); + /** + * 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; - 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 + /** + * 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; - } // namespace lsx + 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 @@ -41345,8 +49350,8 @@ namespace simdjson { #include namespace simdjson { - namespace lsx { - namespace numberparsing { +namespace lsx { +namespace numberparsing { #ifdef JSON_TEST_NUMBERS #define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) @@ -41362,540 +49367,547 @@ namespace simdjson { #define BIGINT_NUMBER(SRC) (BIGINT_ERROR) #endif - namespace { +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(negative)) << 63); - std::memcpy(&d, &mantissa, sizeof(d)); - return d; - } +// 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(negative)) << 63); + std::memcpy(&d, &mantissa, sizeof(d)); + return d; +} - // 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 +// 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) + if (0 <= power && power <= 22 && i <= 9007199254740991) #else - if (-22 <= power && power <= 22 && i <= 9007199254740991) + 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 + { + // 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. + // 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; - } + // 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 - // approximatively 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; + // 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 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. - simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]); - // 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. + // 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 - // 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. - simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); - 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); + // 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. - // 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 - } - } + // 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); - mantissa += mantissa & 1; - mantissa >>= 1; + // 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 + } + } - // 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; - } + mantissa += mantissa & 1; + mantissa >>= 1; - // 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(ptr)); - // We do not accept infinite values. + // 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; +} - // 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::max)() || *outDouble < std::numeric_limits::lowest()); - } +// 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(ptr)); + // We do not accept infinite values. - static bool parse_float_fallback(const uint8_t* ptr, const uint8_t* end_ptr, double* outDouble) { - *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(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::max)() || *outDouble < std::numeric_limits::lowest()); +} - // 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::max)() || *outDouble < std::numeric_limits::lowest()); - } +static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) { + *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(end_ptr)); + // We do not accept infinite values. - // 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); - } + // 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::max)() || *outDouble < std::numeric_limits::lowest()); +} - template - 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(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; - } +// 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); +} - simdjson_inline bool is_digit(const uint8_t c) { - return static_cast(c - '0') <= 9; - } +template +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(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 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; +simdjson_inline bool is_digit(const uint8_t c) { + return static_cast(c - '0') <= 9; +} + +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; #ifdef SIMDJSON_SWAR_NUMBER_PARSING #if SIMDJSON_SWAR_NUMBER_PARSING - // this helps if we have lots of decimals! - // this turns out to be frequent enough. - if (is_made_of_eight_digits_fast(p)) { - i = i * 100000000 + parse_eight_digits_unrolled(p); - p += 8; - } + // this helps if we have lots of decimals! + // this turns out to be frequent enough. + if (is_made_of_eight_digits_fast(p)) { + i = i * 100000000 + parse_eight_digits_unrolled(p); + p += 8; + } #endif // SIMDJSON_SWAR_NUMBER_PARSING #endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING - // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) - if (parse_digit(*p, i)) { ++p; } - while (parse_digit(*p, i)) { p++; } - exponent = first_after_period - p; - // Decimal without digits (123.) is illegal - if (exponent == 0) { - return INVALID_NUMBER(src); - } - return SUCCESS; - } + // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) + if (parse_digit(*p, i)) { ++p; } + while (parse_digit(*p, i)) { p++; } + exponent = first_after_period - p; + // Decimal without digits (123.) is illegal + if (exponent == 0) { + return INVALID_NUMBER(src); + } + return SUCCESS; +} - 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 +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. + // 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. + // 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 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; - } + // 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 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); - } +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 +} // unnamed namespace - /** @private */ - static 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 */ +static 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 - 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; - } +/** @private */ +template +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 - simdjson_inline error_code parse_number(const uint8_t* const src, W& writer); +// 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 +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 +// for performance analysis, it is sometimes useful to skip parsing #ifdef SIMDJSON_SKIPNUMBERPARSING - template - simdjson_inline error_code parse_number(const uint8_t* const, W& writer) { - writer.append_s64(0); // always write zero - return SUCCESS; // always succeeds - } +template +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 parse_unsigned(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline bool is_negative(const uint8_t* src) noexcept { return false; } - simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t* src) noexcept { return false; } - simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t* src) noexcept { return number_type::signed_integer; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t * src) noexcept { return number_type::signed_integer; } #else // parse the number at src @@ -41907,744 +49919,753 @@ namespace simdjson { // content and append a space before calling this function. // // Our objective is accurate parsing (ULP of 0) at high speed. - template - 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)) { 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 INVALID_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 { - WRITE_INTEGER(negative ? (~i + 1) : i, src, writer); - } - 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 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; - 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); - // 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 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 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; - 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); - // 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 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; - 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); - // 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 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 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; - while (parse_digit(*src, i)) { src++; } - - // 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 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; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (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 == '.')) { - p++; - const uint8_t* start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (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 = 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; - } - - simdjson_unused simdjson_inline bool is_negative(const uint8_t* src) noexcept { - return (*src == '-'); - } - - simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t* src) noexcept { - bool negative = (*src == '-'); - src += uint8_t(negative); - const uint8_t* p = src; - while (static_cast(*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 get_number_type(const uint8_t* src) noexcept { - bool negative = (*src == '-'); - src += uint8_t(negative); - const uint8_t* p = src; - while (static_cast(*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("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; - } - 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("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 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 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; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (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 == '.')) { - p++; - const uint8_t* start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (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 = 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; - } - - } // unnamed namespace +template +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)) { 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 INVALID_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 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; + 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); + // 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 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 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; + 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); + // 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 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; + 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); + // 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 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 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; + while (parse_digit(*src, i)) { src++; } + + // 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { + return (*src == '-'); +} + +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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 get_number_type(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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("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("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 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +} // unnamed namespace #endif // SIMDJSON_SKIPNUMBERPARSING - } // namespace numberparsing +} // 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; - } +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 lsx } // namespace simdjson #endif // SIMDJSON_GENERIC_NUMBERPARSING_H @@ -42661,84 +50682,113 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { +namespace lsx { - // - // internal::implementation_simdjson_result_base inline implementation - // +// +// internal::implementation_simdjson_result_base inline implementation +// - template - simdjson_inline void implementation_simdjson_result_base::tie(T& value, error_code& error) && noexcept { - error = this->second; - if (!error) { - value = std::forward>(*this).first; - } - } +template +simdjson_inline void implementation_simdjson_result_base::tie(T &value, error_code &error) && noexcept { + error = this->second; + if (!error) { + value = std::forward>(*this).first; + } +} - template - simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T& value) && noexcept { - error_code error; - std::forward>(*this).tie(value, error); - return error; - } +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T &value) && noexcept { + error_code error; + std::forward>(*this).tie(value, error); + return error; +} - template - simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { - return this->second; - } +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { + return this->second; +} + + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} #if SIMDJSON_EXCEPTIONS - template - simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return this->first; - } +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} - template - simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { - return std::forward>(*this).take_value(); - } +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} - template - simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return std::forward(this->first); - } +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} - template - simdjson_inline implementation_simdjson_result_base::operator T && () && noexcept(false) { - return std::forward>(*this).take_value(); - } + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + +template +simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return this->first; +} + +template +simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { + return std::forward>(*this).take_value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return std::forward(this->first); +} + +template +simdjson_inline implementation_simdjson_result_base::operator T&&() && noexcept(false) { + return std::forward>(*this).take_value(); +} #endif // SIMDJSON_EXCEPTIONS - template - simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { - return this->first; - } +template +simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { + return this->first; +} - template - simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { - return this->first; - } +template +simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { + return this->first; +} - template - simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { - return std::forward(this->first); - } +template +simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { + return std::forward(this->first); +} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T&& value, error_code error) noexcept - : first{ std::forward(value) }, second{ error } {} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept - : implementation_simdjson_result_base(T{}, error) {} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T&& value) noexcept - : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value, error_code error) noexcept + : first{std::forward(value)}, second{error} {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept + : implementation_simdjson_result_base(T{}, error) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value) noexcept + : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} - } // namespace lsx +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H @@ -42769,24 +50819,24 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { +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& 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; - }; +/** + * @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& 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 lsx } // namespace simdjson #endif // SIMDJSON_LSX_IMPLEMENTATION_H @@ -42806,21 +50856,21 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - /** - * Implementation for LSX. - */ - namespace lsx { +/** + * Implementation for LSX. + */ +namespace lsx { - class implementation; +class implementation; - namespace { - namespace simd { - template struct simd8; - template struct simd8x64; - } // namespace simd - } // unnamed namespace +namespace { +namespace simd { +template struct simd8; +template struct simd8x64; +} // namespace simd +} // unnamed namespace - } // namespace lsx +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_LSX_BASE_H @@ -42834,8 +50884,6 @@ namespace simdjson { /* amalgamation skipped (editor-only): #include "simdjson/lsx/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 static_assert(sizeof(__m128i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for LoongArch SX"); @@ -42854,43 +50902,43 @@ static_assert(sizeof(__m128i) <= simdjson::SIMDJSON_PADDING, "insufficient paddi /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace { +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); - } +// 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 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 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); - } +/* 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); +} - simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t* result) { - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); - } +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t *result) { + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); +} - } // unnamed namespace - } // namespace lsx +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_LSX_BITMANIPULATION_H @@ -42905,26 +50953,26 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace { +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; - } +// +// 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 +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif @@ -42943,33 +50991,39 @@ namespace simdjson { #include namespace simdjson { - namespace lsx { - namespace numberparsing { +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); - } +// 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; - } +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 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 */ @@ -42985,347 +51039,348 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace { - namespace simd { +namespace lsx { +namespace { +namespace simd { - // Forward-declared so they can be used by splat and friends. - template - struct base { - __m128i value; + // Forward-declared so they can be used by splat and friends. + template + struct base { + __m128i value; - // Zero constructor - simdjson_inline base() : value{ __m128i() } {} + // Zero constructor + simdjson_inline base() : value{__m128i()} {} - // Conversion from SIMD register - simdjson_inline base(const __m128i _value) : value(_value) {} + // 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; } + // 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(this); *this_cast = *this_cast | other; return *this_cast; } - simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } - simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } - }; + // 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(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; - // Forward-declared so they can be used by splat and friends. - template - struct simd8; + // Forward-declared so they can be used by splat and friends. + template + struct simd8; - template> - struct base8 : base> { - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m128i _value) : base>(_value) {} + template> + struct base8: base> { + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m128i _value) : base>(_value) {} - friend simdjson_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return __lsx_vseq_b(lhs, rhs); } + friend simdjson_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return __lsx_vseq_b(lhs, rhs); } - static const int SIZE = sizeof(base>::value); + static const int SIZE = sizeof(base>::value); - template - simdjson_inline simd8 prev(const simd8 prev_chunk) const { - return __lsx_vor_v(__lsx_vbsll_v(*this, N), __lsx_vbsrl_v(prev_chunk, 16 - N)); - } - }; + template + simdjson_inline simd8 prev(const simd8 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 : base8 { - static simdjson_inline simd8 splat(bool _value) { - return __lsx_vreplgr2vr_b(uint8_t(-(!!_value))); - } + // SIMD byte mask type (returned by things like eq and gt) + template<> + struct simd8: base8 { + static simdjson_inline simd8 splat(bool _value) { + return __lsx_vreplgr2vr_b(uint8_t(-(!!_value))); + } - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m128i _value) : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) : base8(splat(_value)) {} + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m128i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(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 operator~() const { return *this ^ true; } - }; + 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 operator~() const { return *this ^ true; } + }; - template - struct base8_numeric : base8 { - static simdjson_inline simd8 splat(T _value) { return __lsx_vreplgr2vr_b(_value); } - static simdjson_inline simd8 zero() { return __lsx_vldi(0); } - static simdjson_inline simd8 load(const T values[16]) { - return __lsx_vld(reinterpret_cast(values), 0); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdjson_inline simd8 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + template + struct base8_numeric: base8 { + static simdjson_inline simd8 splat(T _value) { return __lsx_vreplgr2vr_b(_value); } + static simdjson_inline simd8 zero() { return __lsx_vldi(0); } + static simdjson_inline simd8 load(const T values[16]) { + return __lsx_vld(reinterpret_cast(values), 0); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - simdjson_inline base8_numeric() : base8() {} - simdjson_inline base8_numeric(const __m128i _value) : base8(_value) {} + simdjson_inline base8_numeric() : base8() {} + simdjson_inline base8_numeric(const __m128i _value) : base8(_value) {} - // Store to array - simdjson_inline void store(T dst[16]) const { - return __lsx_vst(*this, reinterpret_cast<__m128i*>(dst), 0); - } + // 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 operator+(const simd8 other) const { return __lsx_vadd_b(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return __lsx_vsub_b(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { return __lsx_vadd_b(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return __lsx_vsub_b(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } - // Override to distinguish from bool version - simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return __lsx_vshuf_b(lookup_table, lookup_table, *this); - } + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 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 - 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(reinterpret_cast(pshufb_combine_table)), pop1 * 8); - __m128i answer = __lsx_vshuf_b(pruned, pruned, compactmask); - __lsx_vst(answer, reinterpret_cast(output), 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. + template + 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(reinterpret_cast(pshufb_combine_table)), pop1 * 8); + __m128i answer = __lsx_vshuf_b(pruned, pruned, compactmask); + __lsx_vst(answer, reinterpret_cast(output), 0); + } - template - simdjson_inline simd8 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::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - }; + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; - // Signed bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + // Signed bytes + template<> + struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - // Order-sensitive comparisons - simdjson_inline simd8 max_val(const simd8 other) const { return __lsx_vmax_b(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return __lsx_vmin_b(*this, other); } - simdjson_inline simd8 operator>(const simd8 other) const { return __lsx_vslt_b(other, *this); } - simdjson_inline simd8 operator<(const simd8 other) const { return __lsx_vslt_b(*this, other); } - }; + // Order-sensitive comparisons + simdjson_inline simd8 max_val(const simd8 other) const { return __lsx_vmax_b(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return __lsx_vmin_b(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return __lsx_vslt_b(other, *this); } + simdjson_inline simd8 operator<(const simd8 other) const { return __lsx_vslt_b(*this, other); } + }; - // Unsigned bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + // Unsigned bytes + template<> + struct simd8: base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - // Saturated math - simdjson_inline simd8 saturating_add(const simd8 other) const { return __lsx_vsadd_bu(*this, other); } - simdjson_inline simd8 saturating_sub(const simd8 other) const { return __lsx_vssub_bu(*this, other); } + // Saturated math + simdjson_inline simd8 saturating_add(const simd8 other) const { return __lsx_vsadd_bu(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return __lsx_vssub_bu(*this, other); } - // Order-specific operations - simdjson_inline simd8 max_val(const simd8 other) const { return __lsx_vmax_bu(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return __lsx_vmin_bu(other, *this); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } - simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } - simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } - simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } + // Order-specific operations + simdjson_inline simd8 max_val(const simd8 other) const { return __lsx_vmax_bu(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return __lsx_vmin_bu(other, *this); } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } + simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } + simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } + simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } - // Bit-specific operations - simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } - simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } - simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } - simdjson_inline simd8 any_bits_set(simd8 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 bits) const { - return 0 == __lsx_vpickve2gr_hu(__lsx_vmsknz_b(__lsx_vand_v(*this, bits)), 0); - } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } - template - simdjson_inline simd8 shr() const { return simd8(__lsx_vsrli_b(*this, N)); } - template - simdjson_inline simd8 shl() const { return simd8(__lsx_vslli_b(*this, N)); } - }; + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } + simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } + simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } + simdjson_inline simd8 any_bits_set(simd8 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 bits) const { + return 0 == __lsx_vpickve2gr_hu(__lsx_vmsknz_b(__lsx_vand_v(*this, bits)), 0); + } + simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } + template + simdjson_inline simd8 shr() const { return simd8(__lsx_vsrli_b(*this, N)); } + template + simdjson_inline simd8 shl() const { return simd8(__lsx_vslli_b(*this, N)); } + }; - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 4, "LSX kernel should use four registers per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 4, "LSX kernel should use four registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{ chunk0, chunk1, chunk2, chunk3 } {} - simdjson_inline simd8x64(const T ptr[64]) : chunks{ simd8::load(ptr), simd8::load(ptr + 16), simd8::load(ptr + 32), simd8::load(ptr + 48) } {} + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::load(ptr+16), simd8::load(ptr+32), simd8::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(output); - // There should be a critical value which processes in scaler is faster. - if (zcnt1) - this->chunks[0].compress(mask1, reinterpret_cast(voutput)); - voutput += zcnt1; - if (zcnt2) - this->chunks[1].compress(mask2, reinterpret_cast(voutput)); - voutput += zcnt2; - if (zcnt3) - this->chunks[2].compress(mask3, reinterpret_cast(voutput)); - voutput += zcnt3; - if (zcnt4) - this->chunks[3].compress(mask4, reinterpret_cast(voutput)); - voutput += zcnt4; - return reinterpret_cast(voutput) - reinterpret_cast(output); - } + 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(output); + // There should be a critical value which processes in scaler is faster. + if (zcnt1) + this->chunks[0].compress(mask1, reinterpret_cast(voutput)); + voutput += zcnt1; + if (zcnt2) + this->chunks[1].compress(mask2, reinterpret_cast(voutput)); + voutput += zcnt2; + if (zcnt3) + this->chunks[2].compress(mask3, reinterpret_cast(voutput)); + voutput += zcnt3; + if (zcnt4) + this->chunks[3].compress(mask4, reinterpret_cast(voutput)); + voutput += zcnt4; + return reinterpret_cast(voutput) - reinterpret_cast(output); + } - simdjson_inline void store(T ptr[64]) const { - this->chunks[0].store(ptr + sizeof(simd8) * 0); - this->chunks[1].store(ptr + sizeof(simd8) * 1); - this->chunks[2].store(ptr + sizeof(simd8) * 2); - this->chunks[3].store(ptr + sizeof(simd8) * 3); - } + simdjson_inline void store(T ptr[64]) const { + this->chunks[0].store(ptr+sizeof(simd8)*0); + this->chunks[1].store(ptr+sizeof(simd8)*1); + this->chunks[2].store(ptr+sizeof(simd8)*2); + this->chunks[3].store(ptr+sizeof(simd8)*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 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 reduce_or() const { - return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]); - } + simdjson_inline simd8 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] == mask, - this->chunks[1] == mask, - this->chunks[2] == mask, - this->chunks[3] == mask - ).to_bitmask(); - } + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + 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& other) const { - return simd8x64( - 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 eq(const simd8x64 &other) const { + return simd8x64( + 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] <= mask, - this->chunks[1] <= mask, - this->chunks[2] <= mask, - this->chunks[3] <= mask - ).to_bitmask(); - } - }; // struct simd8x64 + simdjson_inline uint64_t lteq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] <= mask, + this->chunks[1] <= mask, + this->chunks[2] <= mask, + this->chunks[3] <= mask + ).to_bitmask(); + } + }; // struct simd8x64 - } // namespace simd - } // unnamed namespace - } // namespace lsx +} // namespace simd +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_LSX_SIMD_H @@ -43342,46 +51397,71 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace { +namespace lsx { +namespace { - using namespace simd; +using namespace simd; - // Holds backslashes and quotes locations. - struct backslash_and_quote { - public: - static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t* src, uint8_t* dst); +// 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); } + 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 + 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 v0(src); - simd8 v1(src + sizeof(v0)); - v0.store(dst); - v1.store(dst + sizeof(v0)); +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 v0(src); + simd8 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(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); - return { - uint32_t(bs_and_quote), // bs_bits - uint32_t(bs_and_quote >> 32) // quote_bits - }; - } + // 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(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); + return { + uint32_t(bs_and_quote), // bs_bits + uint32_t(bs_and_quote >> 32) // quote_bits + }; +} - } // unnamed namespace - } // namespace lsx + +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 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + static_cast((is_backslash | is_quote | is_control).to_bitmask()) + }; +} + +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_LSX_STRINGPARSING_DEFS_H @@ -43406,13 +51486,13 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace { +namespace lsx { +namespace { - struct json_character_block; +struct json_character_block; - } // unnamed namespace - } // namespace lsx +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_BASE_H @@ -43429,14 +51509,14 @@ namespace simdjson { // Interface a dom parser implementation must fulfill namespace simdjson { - namespace lsx { - namespace { +namespace lsx { +namespace { - simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3); - simdjson_inline bool is_ascii(const simd8x64& input); +simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3); +simdjson_inline bool is_ascii(const simd8x64& input); - } // unnamed namespace - } // namespace lsx +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_DOM_PARSER_IMPLEMENTATION_H @@ -43451,22 +51531,22 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace { +namespace lsx { +namespace { - struct json_character_block { - static simdjson_inline json_character_block classify(const simd::simd8x64& in); +struct json_character_block { + static simdjson_inline json_character_block classify(const simd::simd8x64& in); - simdjson_inline uint64_t whitespace() const noexcept { return _whitespace; } - simdjson_inline uint64_t op() const noexcept { return _op; } - simdjson_inline uint64_t scalar() const noexcept { return ~(op() | whitespace()); } + simdjson_inline uint64_t whitespace() const noexcept { return _whitespace; } + simdjson_inline uint64_t op() const noexcept { return _op; } + simdjson_inline uint64_t scalar() const noexcept { return ~(op() | whitespace()); } - uint64_t _whitespace; - uint64_t _op; - }; + uint64_t _whitespace; + uint64_t _op; +}; - } // unnamed namespace - } // namespace lsx +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_JSON_CHARACTER_BLOCK_H @@ -43485,30 +51565,30 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace { - namespace stage1 { +namespace lsx { +namespace { +namespace stage1 { - class bit_indexer; - template - struct buf_block_reader; - struct json_block; - class json_minifier; - class json_scanner; - struct json_string_block; - class json_string_scanner; - class json_structural_indexer; +class bit_indexer; +template +struct buf_block_reader; +struct json_block; +class json_minifier; +class json_scanner; +struct json_string_block; +class json_string_scanner; +class json_structural_indexer; - } // namespace stage1 +} // namespace stage1 - namespace utf8_validation { - struct utf8_checker; - } // namespace utf8_validation +namespace utf8_validation { +struct utf8_checker; +} // namespace utf8_validation - using utf8_validation::utf8_checker; +using utf8_validation::utf8_checker; - } // unnamed namespace - } // namespace lsx +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_BASE_H @@ -43525,109 +51605,109 @@ namespace simdjson { #include namespace simdjson { - namespace lsx { - namespace { - namespace stage1 { +namespace lsx { +namespace { +namespace stage1 { - // Walks through a buffer in block-sized increments, loading the last part with spaces - template - struct buf_block_reader { - public: - simdjson_inline buf_block_reader(const uint8_t* _buf, size_t _len); - simdjson_inline size_t block_index(); - simdjson_inline bool has_full_block() const; - simdjson_inline const uint8_t* full_block() const; - /** - * Get the last block, padded with spaces. - * - * There will always be a last block, with at least 1 byte, unless len == 0 (in which case this - * function fills the buffer with spaces and returns 0. In particular, if len == STEP_SIZE there - * will be 0 full_blocks and 1 remainder block with STEP_SIZE bytes and no spaces for padding. - * - * @return the number of effective characters in the last block. - */ - simdjson_inline size_t get_remainder(uint8_t* dst) const; - simdjson_inline void advance(); - private: - const uint8_t* buf; - const size_t len; - const size_t lenminusstep; - size_t idx; - }; +// Walks through a buffer in block-sized increments, loading the last part with spaces +template +struct buf_block_reader { +public: + simdjson_inline buf_block_reader(const uint8_t *_buf, size_t _len); + simdjson_inline size_t block_index(); + simdjson_inline bool has_full_block() const; + simdjson_inline const uint8_t *full_block() const; + /** + * Get the last block, padded with spaces. + * + * There will always be a last block, with at least 1 byte, unless len == 0 (in which case this + * function fills the buffer with spaces and returns 0. In particular, if len == STEP_SIZE there + * will be 0 full_blocks and 1 remainder block with STEP_SIZE bytes and no spaces for padding. + * + * @return the number of effective characters in the last block. + */ + simdjson_inline size_t get_remainder(uint8_t *dst) const; + simdjson_inline void advance(); +private: + const uint8_t *buf; + const size_t len; + const size_t lenminusstep; + size_t idx; +}; - // Routines to print masks and text for debugging bitmask operations - simdjson_unused static char* format_input_text_64(const uint8_t* text) { - static char buf[sizeof(simd8x64) + 1]; - for (size_t i = 0; i < sizeof(simd8x64); i++) { - buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]); - } - buf[sizeof(simd8x64)] = '\0'; - return buf; - } +// Routines to print masks and text for debugging bitmask operations +simdjson_unused static char * format_input_text_64(const uint8_t *text) { + static char buf[sizeof(simd8x64) + 1]; + for (size_t i=0; i); i++) { + buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]); + } + buf[sizeof(simd8x64)] = '\0'; + return buf; +} - // Routines to print masks and text for debugging bitmask operations - simdjson_unused static char* format_input_text(const simd8x64& in) { - static char buf[sizeof(simd8x64) + 1]; - in.store(reinterpret_cast(buf)); - for (size_t i = 0; i < sizeof(simd8x64); i++) { - if (buf[i] < ' ') { buf[i] = '_'; } - } - buf[sizeof(simd8x64)] = '\0'; - return buf; - } +// Routines to print masks and text for debugging bitmask operations +simdjson_unused static char * format_input_text(const simd8x64& in) { + static char buf[sizeof(simd8x64) + 1]; + in.store(reinterpret_cast(buf)); + for (size_t i=0; i); i++) { + if (buf[i] < ' ') { buf[i] = '_'; } + } + buf[sizeof(simd8x64)] = '\0'; + return buf; +} - simdjson_unused static char* format_input_text(const simd8x64& in, uint64_t mask) { - static char buf[sizeof(simd8x64) + 1]; - in.store(reinterpret_cast(buf)); - for (size_t i = 0; i < sizeof(simd8x64); i++) { - if (buf[i] <= ' ') { buf[i] = '_'; } - if (!(mask & (size_t(1) << i))) { buf[i] = ' '; } - } - buf[sizeof(simd8x64)] = '\0'; - return buf; - } +simdjson_unused static char * format_input_text(const simd8x64& in, uint64_t mask) { + static char buf[sizeof(simd8x64) + 1]; + in.store(reinterpret_cast(buf)); + for (size_t i=0; i); i++) { + if (buf[i] <= ' ') { buf[i] = '_'; } + if (!(mask & (size_t(1) << i))) { buf[i] = ' '; } + } + buf[sizeof(simd8x64)] = '\0'; + return buf; +} - simdjson_unused static char* format_mask(uint64_t mask) { - static char buf[sizeof(simd8x64) + 1]; - for (size_t i = 0; i < 64; i++) { - buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' '; - } - buf[64] = '\0'; - return buf; - } +simdjson_unused static char * format_mask(uint64_t mask) { + static char buf[sizeof(simd8x64) + 1]; + for (size_t i=0; i<64; i++) { + buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' '; + } + buf[64] = '\0'; + return buf; +} - template - simdjson_inline buf_block_reader::buf_block_reader(const uint8_t* _buf, size_t _len) : buf{ _buf }, len{ _len }, lenminusstep{ len < STEP_SIZE ? 0 : len - STEP_SIZE }, idx{ 0 } {} +template +simdjson_inline buf_block_reader::buf_block_reader(const uint8_t *_buf, size_t _len) : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE}, idx{0} {} - template - simdjson_inline size_t buf_block_reader::block_index() { return idx; } +template +simdjson_inline size_t buf_block_reader::block_index() { return idx; } - template - simdjson_inline bool buf_block_reader::has_full_block() const { - return idx < lenminusstep; - } +template +simdjson_inline bool buf_block_reader::has_full_block() const { + return idx < lenminusstep; +} - template - simdjson_inline const uint8_t* buf_block_reader::full_block() const { - return &buf[idx]; - } +template +simdjson_inline const uint8_t *buf_block_reader::full_block() const { + return &buf[idx]; +} - template - simdjson_inline size_t buf_block_reader::get_remainder(uint8_t* dst) const { - if (len == idx) { return 0; } // memcpy(dst, null, 0) will trigger an error with some sanitizers - std::memset(dst, 0x20, STEP_SIZE); // std::memset STEP_SIZE because it's more efficient to write out 8 or 16 bytes at once. - std::memcpy(dst, buf + idx, len - idx); - return len - idx; - } +template +simdjson_inline size_t buf_block_reader::get_remainder(uint8_t *dst) const { + if(len == idx) { return 0; } // memcpy(dst, null, 0) will trigger an error with some sanitizers + std::memset(dst, 0x20, STEP_SIZE); // std::memset STEP_SIZE because it's more efficient to write out 8 or 16 bytes at once. + std::memcpy(dst, buf + idx, len - idx); + return len - idx; +} - template - simdjson_inline void buf_block_reader::advance() { - idx += STEP_SIZE; - } +template +simdjson_inline void buf_block_reader::advance() { + idx += STEP_SIZE; +} - } // namespace stage1 - } // unnamed namespace - } // namespace lsx +} // namespace stage1 +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_BUF_BLOCK_READER_H @@ -43643,145 +51723,145 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace { - namespace stage1 { +namespace lsx { +namespace { +namespace stage1 { - /** - * Scans for escape characters in JSON, taking care with multiple backslashes (\\n vs. \n). - */ - struct json_escape_scanner { - /** The actual escape characters (the backslashes themselves). */ - uint64_t next_is_escaped = 0ULL; +/** + * Scans for escape characters in JSON, taking care with multiple backslashes (\\n vs. \n). + */ +struct json_escape_scanner { + /** The actual escape characters (the backslashes themselves). */ + uint64_t next_is_escaped = 0ULL; - struct escaped_and_escape { - /** - * Mask of escaped characters. - * - * ``` - * \n \\n \\\n \\\\n \ - * 0100100010100101000 - * n \ \ n \ \ - * ``` - */ - uint64_t escaped; - /** - * Mask of escape characters. - * - * ``` - * \n \\n \\\n \\\\n \ - * 1001000101001010001 - * \ \ \ \ \ \ \ - * ``` - */ - uint64_t escape; - }; + struct escaped_and_escape { + /** + * Mask of escaped characters. + * + * ``` + * \n \\n \\\n \\\\n \ + * 0100100010100101000 + * n \ \ n \ \ + * ``` + */ + uint64_t escaped; + /** + * Mask of escape characters. + * + * ``` + * \n \\n \\\n \\\\n \ + * 1001000101001010001 + * \ \ \ \ \ \ \ + * ``` + */ + uint64_t escape; + }; - /** - * Get a mask of both escape and escaped characters (the characters following a backslash). - * - * @param potential_escape A mask of the character that can escape others (but could be - * escaped itself). e.g. block.eq('\\') - */ - simdjson_really_inline escaped_and_escape next(uint64_t backslash) noexcept { + /** + * Get a mask of both escape and escaped characters (the characters following a backslash). + * + * @param potential_escape A mask of the character that can escape others (but could be + * escaped itself). e.g. block.eq('\\') + */ + simdjson_really_inline escaped_and_escape next(uint64_t backslash) noexcept { #if !SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT - if (!backslash) { return { next_escaped_without_backslashes(), 0 }; } + if (!backslash) { return {next_escaped_without_backslashes(), 0}; } #endif - // | | Mask (shows characters instead of 1's) | Depth | Instructions | - // |--------------------------------|----------------------------------------|-------|---------------------| - // | string | `\\n_\\\n___\\\n___\\\\___\\\\__\\\` | | | - // | | ` even odd even odd odd` | | | - // | potential_escape | ` \ \\\ \\\ \\\\ \\\\ \\\` | 1 | 1 (backslash & ~first_is_escaped) - // | escape_and_terminal_code | ` \n \ \n \ \n \ \ \ \ \ \` | 5 | 5 (next_escape_and_terminal_code()) - // | escaped | `\ \ n \ n \ \ \ \ \ ` X | 6 | 7 (escape_and_terminal_code ^ (potential_escape | first_is_escaped)) - // | escape | ` \ \ \ \ \ \ \ \ \ \` | 6 | 8 (escape_and_terminal_code & backslash) - // | first_is_escaped | `\ ` | 7 (*) | 9 (escape >> 63) () - // (*) this is not needed until the next iteration - uint64_t escape_and_terminal_code = next_escape_and_terminal_code(backslash & ~this->next_is_escaped); - uint64_t escaped = escape_and_terminal_code ^ (backslash | this->next_is_escaped); - uint64_t escape = escape_and_terminal_code & backslash; - this->next_is_escaped = escape >> 63; - return { escaped, escape }; - } + // | | Mask (shows characters instead of 1's) | Depth | Instructions | + // |--------------------------------|----------------------------------------|-------|---------------------| + // | string | `\\n_\\\n___\\\n___\\\\___\\\\__\\\` | | | + // | | ` even odd even odd odd` | | | + // | potential_escape | ` \ \\\ \\\ \\\\ \\\\ \\\` | 1 | 1 (backslash & ~first_is_escaped) + // | escape_and_terminal_code | ` \n \ \n \ \n \ \ \ \ \ \` | 5 | 5 (next_escape_and_terminal_code()) + // | escaped | `\ \ n \ n \ \ \ \ \ ` X | 6 | 7 (escape_and_terminal_code ^ (potential_escape | first_is_escaped)) + // | escape | ` \ \ \ \ \ \ \ \ \ \` | 6 | 8 (escape_and_terminal_code & backslash) + // | first_is_escaped | `\ ` | 7 (*) | 9 (escape >> 63) () + // (*) this is not needed until the next iteration + uint64_t escape_and_terminal_code = next_escape_and_terminal_code(backslash & ~this->next_is_escaped); + uint64_t escaped = escape_and_terminal_code ^ (backslash | this->next_is_escaped); + uint64_t escape = escape_and_terminal_code & backslash; + this->next_is_escaped = escape >> 63; + return {escaped, escape}; + } - private: - static constexpr const uint64_t ODD_BITS = 0xAAAAAAAAAAAAAAAAULL; +private: + static constexpr const uint64_t ODD_BITS = 0xAAAAAAAAAAAAAAAAULL; - simdjson_really_inline uint64_t next_escaped_without_backslashes() noexcept { - uint64_t escaped = this->next_is_escaped; - this->next_is_escaped = 0; - return escaped; - } + simdjson_really_inline uint64_t next_escaped_without_backslashes() noexcept { + uint64_t escaped = this->next_is_escaped; + this->next_is_escaped = 0; + return escaped; + } - /** - * Returns a mask of the next escape characters (masking out escaped backslashes), along with - * any non-backslash escape codes. - * - * \n \\n \\\n \\\\n returns: - * \n \ \ \n \ \ - * 11 100 1011 10100 - * - * You are expected to mask out the first bit yourself if the previous block had a trailing - * escape. - * - * & the result with potential_escape to get just the escape characters. - * ^ the result with (potential_escape | first_is_escaped) to get escaped characters. - */ - static simdjson_really_inline uint64_t next_escape_and_terminal_code(uint64_t potential_escape) noexcept { - // If we were to just shift and mask out any odd bits, we'd actually get a *half* right answer: - // any even-aligned backslash runs would be correct! Odd-aligned backslash runs would be - // inverted (\\\ would be 010 instead of 101). - // - // ``` - // string: | ____\\\\_\\\\_____ | - // maybe_escaped | ODD | \ \ \ \ | - // even-aligned ^^^ ^^^^ odd-aligned - // ``` - // - // Taking that into account, our basic strategy is: - // - // 1. Use subtraction to produce a mask with 1's for even-aligned runs and 0's for - // odd-aligned runs. - // 2. XOR all odd bits, which masks out the odd bits in even-aligned runs, and brings IN the - // odd bits in odd-aligned runs. - // 3. & with backslash to clean up any stray bits. - // runs are set to 0, and then XORing with "odd": - // - // | | Mask (shows characters instead of 1's) | Instructions | - // |--------------------------------|----------------------------------------|---------------------| - // | string | `\\n_\\\n___\\\n___\\\\___\\\\__\\\` | - // | | ` even odd even odd odd` | - // | maybe_escaped | ` n \\n \\n \\\_ \\\_ \\` X | 1 (potential_escape << 1) - // | maybe_escaped_and_odd | ` \n_ \\n _ \\\n_ _ \\\__ _\\\_ \\\` | 1 (maybe_escaped | odd) - // | even_series_codes_and_odd | ` n_\\\ _ n_ _\\\\ _ _ ` | 1 (maybe_escaped_and_odd - potential_escape) - // | escape_and_terminal_code | ` \n \ \n \ \n \ \ \ \ \ \` | 1 (^ odd) - // + /** + * Returns a mask of the next escape characters (masking out escaped backslashes), along with + * any non-backslash escape codes. + * + * \n \\n \\\n \\\\n returns: + * \n \ \ \n \ \ + * 11 100 1011 10100 + * + * You are expected to mask out the first bit yourself if the previous block had a trailing + * escape. + * + * & the result with potential_escape to get just the escape characters. + * ^ the result with (potential_escape | first_is_escaped) to get escaped characters. + */ + static simdjson_really_inline uint64_t next_escape_and_terminal_code(uint64_t potential_escape) noexcept { + // If we were to just shift and mask out any odd bits, we'd actually get a *half* right answer: + // any even-aligned backslash runs would be correct! Odd-aligned backslash runs would be + // inverted (\\\ would be 010 instead of 101). + // + // ``` + // string: | ____\\\\_\\\\_____ | + // maybe_escaped | ODD | \ \ \ \ | + // even-aligned ^^^ ^^^^ odd-aligned + // ``` + // + // Taking that into account, our basic strategy is: + // + // 1. Use subtraction to produce a mask with 1's for even-aligned runs and 0's for + // odd-aligned runs. + // 2. XOR all odd bits, which masks out the odd bits in even-aligned runs, and brings IN the + // odd bits in odd-aligned runs. + // 3. & with backslash to clean up any stray bits. + // runs are set to 0, and then XORing with "odd": + // + // | | Mask (shows characters instead of 1's) | Instructions | + // |--------------------------------|----------------------------------------|---------------------| + // | string | `\\n_\\\n___\\\n___\\\\___\\\\__\\\` | + // | | ` even odd even odd odd` | + // | maybe_escaped | ` n \\n \\n \\\_ \\\_ \\` X | 1 (potential_escape << 1) + // | maybe_escaped_and_odd | ` \n_ \\n _ \\\n_ _ \\\__ _\\\_ \\\` | 1 (maybe_escaped | odd) + // | even_series_codes_and_odd | ` n_\\\ _ n_ _\\\\ _ _ ` | 1 (maybe_escaped_and_odd - potential_escape) + // | escape_and_terminal_code | ` \n \ \n \ \n \ \ \ \ \ \` | 1 (^ odd) + // - // Escaped characters are characters following an escape. - uint64_t maybe_escaped = potential_escape << 1; + // Escaped characters are characters following an escape. + uint64_t maybe_escaped = potential_escape << 1; - // To distinguish odd from even escape sequences, therefore, we turn on any *starting* - // escapes that are on an odd byte. (We actually bring in all odd bits, for speed.) - // - Odd runs of backslashes are 0000, and the code at the end ("n" in \n or \\n) is 1. - // - Odd runs of backslashes are 1111, and the code at the end ("n" in \n or \\n) is 0. - // - All other odd bytes are 1, and even bytes are 0. - uint64_t maybe_escaped_and_odd_bits = maybe_escaped | ODD_BITS; - uint64_t even_series_codes_and_odd_bits = maybe_escaped_and_odd_bits - potential_escape; + // To distinguish odd from even escape sequences, therefore, we turn on any *starting* + // escapes that are on an odd byte. (We actually bring in all odd bits, for speed.) + // - Odd runs of backslashes are 0000, and the code at the end ("n" in \n or \\n) is 1. + // - Odd runs of backslashes are 1111, and the code at the end ("n" in \n or \\n) is 0. + // - All other odd bytes are 1, and even bytes are 0. + uint64_t maybe_escaped_and_odd_bits = maybe_escaped | ODD_BITS; + uint64_t even_series_codes_and_odd_bits = maybe_escaped_and_odd_bits - potential_escape; - // Now we flip all odd bytes back with xor. This: - // - Makes odd runs of backslashes go from 0000 to 1010 - // - Makes even runs of backslashes go from 1111 to 1010 - // - Sets actually-escaped codes to 1 (the n in \n and \\n: \n = 11, \\n = 100) - // - Resets all other bytes to 0 - return even_series_codes_and_odd_bits ^ ODD_BITS; - } - }; + // Now we flip all odd bytes back with xor. This: + // - Makes odd runs of backslashes go from 0000 to 1010 + // - Makes even runs of backslashes go from 1111 to 1010 + // - Sets actually-escaped codes to 1 (the n in \n and \\n: \n = 11, \\n = 100) + // - Resets all other bytes to 0 + return even_series_codes_and_odd_bits ^ ODD_BITS; + } +}; - } // namespace stage1 - } // unnamed namespace - } // namespace lsx +} // namespace stage1 +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRING_SCANNER_H @@ -43797,93 +51877,93 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace { - namespace stage1 { +namespace lsx { +namespace { +namespace stage1 { - struct json_string_block { - // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 - simdjson_really_inline json_string_block(uint64_t escaped, uint64_t quote, uint64_t in_string) : - _escaped(escaped), _quote(quote), _in_string(in_string) {} +struct json_string_block { + // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 + simdjson_really_inline json_string_block(uint64_t escaped, uint64_t quote, uint64_t in_string) : + _escaped(escaped), _quote(quote), _in_string(in_string) {} - // Escaped characters (characters following an escape() character) - simdjson_really_inline uint64_t escaped() const { return _escaped; } - // Real (non-backslashed) quotes - simdjson_really_inline uint64_t quote() const { return _quote; } - // Only characters inside the string (not including the quotes) - simdjson_really_inline uint64_t string_content() const { return _in_string & ~_quote; } - // Return a mask of whether the given characters are inside a string (only works on non-quotes) - simdjson_really_inline uint64_t non_quote_inside_string(uint64_t mask) const { return mask & _in_string; } - // Return a mask of whether the given characters are inside a string (only works on non-quotes) - simdjson_really_inline uint64_t non_quote_outside_string(uint64_t mask) const { return mask & ~_in_string; } - // Tail of string (everything except the start quote) - simdjson_really_inline uint64_t string_tail() const { return _in_string ^ _quote; } + // Escaped characters (characters following an escape() character) + simdjson_really_inline uint64_t escaped() const { return _escaped; } + // Real (non-backslashed) quotes + simdjson_really_inline uint64_t quote() const { return _quote; } + // Only characters inside the string (not including the quotes) + simdjson_really_inline uint64_t string_content() const { return _in_string & ~_quote; } + // Return a mask of whether the given characters are inside a string (only works on non-quotes) + simdjson_really_inline uint64_t non_quote_inside_string(uint64_t mask) const { return mask & _in_string; } + // Return a mask of whether the given characters are inside a string (only works on non-quotes) + simdjson_really_inline uint64_t non_quote_outside_string(uint64_t mask) const { return mask & ~_in_string; } + // Tail of string (everything except the start quote) + simdjson_really_inline uint64_t string_tail() const { return _in_string ^ _quote; } - // escaped characters (backslashed--does not include the hex characters after \u) - uint64_t _escaped; - // real quotes (non-escaped ones) - uint64_t _quote; - // string characters (includes start quote but not end quote) - uint64_t _in_string; - }; + // escaped characters (backslashed--does not include the hex characters after \u) + uint64_t _escaped; + // real quotes (non-escaped ones) + uint64_t _quote; + // string characters (includes start quote but not end quote) + uint64_t _in_string; +}; - // Scans blocks for string characters, storing the state necessary to do so - class json_string_scanner { - public: - simdjson_really_inline json_string_block next(const simd::simd8x64& in); - // Returns either UNCLOSED_STRING or SUCCESS - simdjson_really_inline error_code finish(); +// Scans blocks for string characters, storing the state necessary to do so +class json_string_scanner { +public: + simdjson_really_inline json_string_block next(const simd::simd8x64& in); + // Returns either UNCLOSED_STRING or SUCCESS + simdjson_really_inline error_code finish(); - private: - // Scans for escape characters - json_escape_scanner escape_scanner{}; - // Whether the last iteration was still inside a string (all 1's = true, all 0's = false). - uint64_t prev_in_string = 0ULL; - }; +private: + // Scans for escape characters + json_escape_scanner escape_scanner{}; + // Whether the last iteration was still inside a string (all 1's = true, all 0's = false). + uint64_t prev_in_string = 0ULL; +}; - // - // Return a mask of all string characters plus end quotes. - // - // prev_escaped is overflow saying whether the next character is escaped. - // prev_in_string is overflow saying whether we're still in a string. - // - // Backslash sequences outside of quotes will be detected in stage 2. - // - simdjson_really_inline json_string_block json_string_scanner::next(const simd::simd8x64& in) { - const uint64_t backslash = in.eq('\\'); - const uint64_t escaped = escape_scanner.next(backslash).escaped; - const uint64_t quote = in.eq('"') & ~escaped; +// +// Return a mask of all string characters plus end quotes. +// +// prev_escaped is overflow saying whether the next character is escaped. +// prev_in_string is overflow saying whether we're still in a string. +// +// Backslash sequences outside of quotes will be detected in stage 2. +// +simdjson_really_inline json_string_block json_string_scanner::next(const simd::simd8x64& in) { + const uint64_t backslash = in.eq('\\'); + const uint64_t escaped = escape_scanner.next(backslash).escaped; + const uint64_t quote = in.eq('"') & ~escaped; - // - // prefix_xor flips on bits inside the string (and flips off the end quote). - // - // Then we xor with prev_in_string: if we were in a string already, its effect is flipped - // (characters inside strings are outside, and characters outside strings are inside). - // - const uint64_t in_string = prefix_xor(quote) ^ prev_in_string; + // + // prefix_xor flips on bits inside the string (and flips off the end quote). + // + // Then we xor with prev_in_string: if we were in a string already, its effect is flipped + // (characters inside strings are outside, and characters outside strings are inside). + // + const uint64_t in_string = prefix_xor(quote) ^ prev_in_string; - // - // Check if we're still in a string at the end of the box so the next block will know - // - prev_in_string = uint64_t(static_cast(in_string) >> 63); + // + // Check if we're still in a string at the end of the box so the next block will know + // + prev_in_string = uint64_t(static_cast(in_string) >> 63); - // Use ^ to turn the beginning quote off, and the end quote on. + // Use ^ to turn the beginning quote off, and the end quote on. - // We are returning a function-local object so either we get a move constructor - // or we get copy elision. - return json_string_block(escaped, quote, in_string); - } + // We are returning a function-local object so either we get a move constructor + // or we get copy elision. + return json_string_block(escaped, quote, in_string); +} - simdjson_really_inline error_code json_string_scanner::finish() { - if (prev_in_string) { - return UNCLOSED_STRING; - } - return SUCCESS; - } +simdjson_really_inline error_code json_string_scanner::finish() { + if (prev_in_string) { + return UNCLOSED_STRING; + } + return SUCCESS; +} - } // namespace stage1 - } // unnamed namespace - } // namespace lsx +} // namespace stage1 +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRING_SCANNER_H @@ -43899,206 +51979,203 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace { - namespace utf8_validation { +namespace lsx { +namespace { +namespace utf8_validation { - using namespace simd; +using namespace simd; - simdjson_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { - // Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) - // Bit 1 = Too Long (ASCII followed by continuation) - // Bit 2 = Overlong 3-byte - // Bit 4 = Surrogate - // Bit 5 = Overlong 2-byte - // Bit 7 = Two Continuations - constexpr const uint8_t TOO_SHORT = 1 << 0; // 11______ 0_______ - // 11______ 11______ - constexpr const uint8_t TOO_LONG = 1 << 1; // 0_______ 10______ - constexpr const uint8_t OVERLONG_3 = 1 << 2; // 11100000 100_____ - constexpr const uint8_t SURROGATE = 1 << 4; // 11101101 101_____ - constexpr const uint8_t OVERLONG_2 = 1 << 5; // 1100000_ 10______ - constexpr const uint8_t TWO_CONTS = 1 << 7; // 10______ 10______ - constexpr const uint8_t TOO_LARGE = 1 << 3; // 11110100 1001____ - // 11110100 101_____ - // 11110101 1001____ - // 11110101 101_____ - // 1111011_ 1001____ - // 1111011_ 101_____ - // 11111___ 1001____ - // 11111___ 101_____ - constexpr const uint8_t TOO_LARGE_1000 = 1 << 6; - // 11110101 1000____ - // 1111011_ 1000____ - // 11111___ 1000____ - constexpr const uint8_t OVERLONG_4 = 1 << 6; // 11110000 1000____ + simdjson_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { +// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) +// Bit 1 = Too Long (ASCII followed by continuation) +// Bit 2 = Overlong 3-byte +// Bit 4 = Surrogate +// Bit 5 = Overlong 2-byte +// Bit 7 = Two Continuations + constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______ + // 11______ 11______ + constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______ + constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____ + constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____ + constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______ + constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______ + constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____ + // 11110100 101_____ + // 11110101 1001____ + // 11110101 101_____ + // 1111011_ 1001____ + // 1111011_ 101_____ + // 11111___ 1001____ + // 11111___ 101_____ + constexpr const uint8_t TOO_LARGE_1000 = 1<<6; + // 11110101 1000____ + // 1111011_ 1000____ + // 11111___ 1000____ + constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____ - const simd8 byte_1_high = prev1.shr<4>().lookup_16( - // 0_______ ________ - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - // 10______ ________ - TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, - // 1100____ ________ - TOO_SHORT | OVERLONG_2, - // 1101____ ________ - TOO_SHORT, - // 1110____ ________ - TOO_SHORT | OVERLONG_3 | SURROGATE, - // 1111____ ________ - TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 - ); - constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . - const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( - // ____0000 ________ - CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, - // ____0001 ________ - CARRY | OVERLONG_2, - // ____001_ ________ - CARRY, - CARRY, + const simd8 byte_1_high = prev1.shr<4>().lookup_16( + // 0_______ ________ + TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, + TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, + // 10______ ________ + TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, + // 1100____ ________ + TOO_SHORT | OVERLONG_2, + // 1101____ ________ + TOO_SHORT, + // 1110____ ________ + TOO_SHORT | OVERLONG_3 | SURROGATE, + // 1111____ ________ + TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 + ); + constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . + const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( + // ____0000 ________ + CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, + // ____0001 ________ + CARRY | OVERLONG_2, + // ____001_ ________ + CARRY, + CARRY, - // ____0100 ________ - CARRY | TOO_LARGE, - // ____0101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____011_ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, + // ____0100 ________ + CARRY | TOO_LARGE, + // ____0101 ________ + CARRY | TOO_LARGE | TOO_LARGE_1000, + // ____011_ ________ + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____1___ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____1101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000 - ); - const simd8 byte_2_high = input.shr<4>().lookup_16( - // ________ 0_______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, + // ____1___ ________ + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + // ____1101 ________ + CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000 + ); + const simd8 byte_2_high = input.shr<4>().lookup_16( + // ________ 0_______ + TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, + TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - // ________ 1000____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, - // ________ 1001____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, - // ________ 101_____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, + // ________ 1000____ + TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, + // ________ 1001____ + TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, + // ________ 101_____ + TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, + TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - // ________ 11______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT - ); - return (byte_1_high & byte_1_low & byte_2_high); - } - simdjson_inline simd8 check_multibyte_lengths(const simd8 input, - const simd8 prev_input, const simd8 sc) { - simd8 prev2 = input.prev<2>(prev_input); - simd8 prev3 = input.prev<3>(prev_input); - simd8 must23 = must_be_2_3_continuation(prev2, prev3); - simd8 must23_80 = must23 & uint8_t(0x80); - return must23_80 ^ sc; - } + // ________ 11______ + TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT + ); + return (byte_1_high & byte_1_low & byte_2_high); + } + simdjson_inline simd8 check_multibyte_lengths(const simd8 input, + const simd8 prev_input, const simd8 sc) { + simd8 prev2 = input.prev<2>(prev_input); + simd8 prev3 = input.prev<3>(prev_input); + simd8 must23 = must_be_2_3_continuation(prev2, prev3); + simd8 must23_80 = must23 & uint8_t(0x80); + return must23_80 ^ sc; + } - // - // Return nonzero if there are incomplete multibyte characters at the end of the block: - // e.g. if there is a 4-byte character, but it's 3 bytes from the end. - // - simdjson_inline simd8 is_incomplete(const simd8 input) { - // If the previous input's last 3 bytes match this, they're too short (they ended at EOF): - // ... 1111____ 111_____ 11______ -#if SIMDJSON_IMPLEMENTATION_ICELAKE - static const uint8_t max_array[64] = { - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0xf0u - 1, 0xe0u - 1, 0xc0u - 1 - }; + // + // Return nonzero if there are incomplete multibyte characters at the end of the block: + // e.g. if there is a 4-byte character, but it's 3 bytes from the end. + // + simdjson_inline simd8 is_incomplete(const simd8 input) { + // If the previous input's last 3 bytes match this, they're too short (they ended at EOF): + // ... 1111____ 111_____ 11______ +#if SIMDJSON_IMPLEMENTATION_ICELAKE || (SIMDJSON_IMPLEMENTATION_RVV_VLS && __riscv_v_fixed_vlen >= 512) + static const uint8_t max_array[64] = { + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0xf0u-1, 0xe0u-1, 0xc0u-1 + }; #else - static const uint8_t max_array[32] = { - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0xf0u - 1, 0xe0u - 1, 0xc0u - 1 - }; + static const uint8_t max_array[32] = { + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0xf0u-1, 0xe0u-1, 0xc0u-1 + }; #endif - const simd8 max_value(&max_array[sizeof(max_array) - sizeof(simd8)]); - return input.gt_bits(max_value); - } + const simd8 max_value(&max_array[sizeof(max_array)-sizeof(simd8)]); + return input.gt_bits(max_value); + } - struct utf8_checker { - // If this is nonzero, there has been a UTF-8 error. - simd8 error; - // The last input we received - simd8 prev_input_block; - // Whether the last input we received was incomplete (used for ASCII fast path) - simd8 prev_incomplete; + struct utf8_checker { + // If this is nonzero, there has been a UTF-8 error. + simd8 error; + // The last input we received + simd8 prev_input_block; + // Whether the last input we received was incomplete (used for ASCII fast path) + simd8 prev_incomplete; - // - // Check whether the current bytes are valid UTF-8. - // - simdjson_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { - // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes - // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) - simd8 prev1 = input.prev<1>(prev_input); - simd8 sc = check_special_cases(input, prev1); - this->error |= check_multibyte_lengths(input, prev_input, sc); - } + // + // Check whether the current bytes are valid UTF-8. + // + simdjson_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { + // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes + // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) + simd8 prev1 = input.prev<1>(prev_input); + simd8 sc = check_special_cases(input, prev1); + this->error |= check_multibyte_lengths(input, prev_input, sc); + } - // The only problem that can happen at EOF is that a multibyte character is too short - // or a byte value too large in the last bytes: check_special_cases only checks for bytes - // too large in the first of two bytes. - simdjson_inline void check_eof() { - // If the previous block had incomplete UTF-8 characters at the end, an ASCII block can't - // possibly finish them. - this->error |= this->prev_incomplete; - } + // The only problem that can happen at EOF is that a multibyte character is too short + // or a byte value too large in the last bytes: check_special_cases only checks for bytes + // too large in the first of two bytes. + simdjson_inline void check_eof() { + // If the previous block had incomplete UTF-8 characters at the end, an ASCII block can't + // possibly finish them. + this->error |= this->prev_incomplete; + } - simdjson_inline void check_next_input(const simd8x64& input) { - if (simdjson_likely(is_ascii(input))) { - this->error |= this->prev_incomplete; - } - else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 1) - || (simd8x64::NUM_CHUNKS == 2) - || (simd8x64::NUM_CHUNKS == 4), - "We support one, two or four chunks per 64-byte block."); - SIMDJSON_IF_CONSTEXPR(simd8x64::NUM_CHUNKS == 1) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - } -else SIMDJSON_IF_CONSTEXPR(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); + simdjson_inline void check_next_input(const simd8x64& input) { + if(simdjson_likely(is_ascii(input))) { + this->error |= this->prev_incomplete; + } else { + // you might think that a for-loop would work, but under Visual Studio, it is not good enough. + static_assert((simd8x64::NUM_CHUNKS == 1) + ||(simd8x64::NUM_CHUNKS == 2) + || (simd8x64::NUM_CHUNKS == 4), + "We support one, two or four chunks per 64-byte block."); + SIMDJSON_IF_CONSTEXPR (simd8x64::NUM_CHUNKS == 1) { + this->check_utf8_bytes(input.get<0>(), this->prev_input_block); + } else SIMDJSON_IF_CONSTEXPR (simd8x64::NUM_CHUNKS == 2) { + this->check_utf8_bytes(input.get<0>(), this->prev_input_block); + this->check_utf8_bytes(input.get<1>(), input.get<0>()); + } else SIMDJSON_IF_CONSTEXPR (simd8x64::NUM_CHUNKS == 4) { + this->check_utf8_bytes(input.get<0>(), this->prev_input_block); + this->check_utf8_bytes(input.get<1>(), input.get<0>()); + this->check_utf8_bytes(input.get<2>(), input.get<1>()); + this->check_utf8_bytes(input.get<3>(), input.get<2>()); } - else SIMDJSON_IF_CONSTEXPR(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - this->prev_incomplete = is_incomplete(input.chunks[simd8x64::NUM_CHUNKS - 1]); - this->prev_input_block = input.chunks[simd8x64::NUM_CHUNKS - 1]; - } - } - // do not forget to call check_eof! - simdjson_inline error_code errors() { - return this->error.any_bits_set_anywhere() ? error_code::UTF8_ERROR : error_code::SUCCESS; - } + this->prev_incomplete = is_incomplete(input.get::NUM_CHUNKS-1>()); + this->prev_input_block = input.get::NUM_CHUNKS-1>(); + } + } + // do not forget to call check_eof! + simdjson_warn_unused simdjson_inline error_code errors() { + return this->error.any_bits_set_anywhere() ? error_code::UTF8_ERROR : error_code::SUCCESS; + } - }; // struct utf8_checker - } // namespace utf8_validation + }; // struct utf8_checker +} // namespace utf8_validation - } // unnamed namespace - } // namespace lsx +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_UTF8_LOOKUP4_ALGORITHM_H @@ -44115,161 +52192,161 @@ else SIMDJSON_IF_CONSTEXPR(simd8x64::NUM_CHUNKS == 2) { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace { - namespace stage1 { +namespace lsx { +namespace { +namespace stage1 { - /** - * A block of scanned json, with information on operators and scalars. - * - * We seek to identify pseudo-structural characters. Anything that is inside - * a string must be omitted (hence & ~_string.string_tail()). - * Otherwise, pseudo-structural characters come in two forms. - * 1. We have the structural characters ([,],{,},:, comma). The - * term 'structural character' is from the JSON RFC. - * 2. We have the 'scalar pseudo-structural characters'. - * Scalars are quotes, and any character except structural characters and white space. - * - * To identify the scalar pseudo-structural characters, we must look at what comes - * before them: it must be a space, a quote or a structural characters. - * Starting with simdjson v0.3, we identify them by - * negation: we identify everything that is followed by a non-quote scalar, - * and we negate that. Whatever remains must be a 'scalar pseudo-structural character'. - */ - struct json_block { - public: - // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 - simdjson_inline json_block(json_string_block&& string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : - _string(std::move(string)), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} - simdjson_inline json_block(json_string_block string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : - _string(string), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} +/** + * A block of scanned json, with information on operators and scalars. + * + * We seek to identify pseudo-structural characters. Anything that is inside + * a string must be omitted (hence & ~_string.string_tail()). + * Otherwise, pseudo-structural characters come in two forms. + * 1. We have the structural characters ([,],{,},:, comma). The + * term 'structural character' is from the JSON RFC. + * 2. We have the 'scalar pseudo-structural characters'. + * Scalars are quotes, and any character except structural characters and white space. + * + * To identify the scalar pseudo-structural characters, we must look at what comes + * before them: it must be a space, a quote or a structural characters. + * Starting with simdjson v0.3, we identify them by + * negation: we identify everything that is followed by a non-quote scalar, + * and we negate that. Whatever remains must be a 'scalar pseudo-structural character'. + */ +struct json_block { +public: + // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 + simdjson_inline json_block(json_string_block&& string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : + _string(std::move(string)), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} + simdjson_inline json_block(json_string_block string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : + _string(string), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} - /** - * The start of structurals. - * In simdjson prior to v0.3, these were called the pseudo-structural characters. - **/ - simdjson_inline uint64_t structural_start() const noexcept { return potential_structural_start() & ~_string.string_tail(); } - /** All JSON whitespace (i.e. not in a string) */ - simdjson_inline uint64_t whitespace() const noexcept { return non_quote_outside_string(_characters.whitespace()); } + /** + * The start of structurals. + * In simdjson prior to v0.3, these were called the pseudo-structural characters. + **/ + simdjson_inline uint64_t structural_start() const noexcept { return potential_structural_start() & ~_string.string_tail(); } + /** All JSON whitespace (i.e. not in a string) */ + simdjson_inline uint64_t whitespace() const noexcept { return non_quote_outside_string(_characters.whitespace()); } - // Helpers + // Helpers - /** Whether the given characters are inside a string (only works on non-quotes) */ - simdjson_inline uint64_t non_quote_inside_string(uint64_t mask) const noexcept { return _string.non_quote_inside_string(mask); } - /** Whether the given characters are outside a string (only works on non-quotes) */ - simdjson_inline uint64_t non_quote_outside_string(uint64_t mask) const noexcept { return _string.non_quote_outside_string(mask); } + /** Whether the given characters are inside a string (only works on non-quotes) */ + simdjson_inline uint64_t non_quote_inside_string(uint64_t mask) const noexcept { return _string.non_quote_inside_string(mask); } + /** Whether the given characters are outside a string (only works on non-quotes) */ + simdjson_inline uint64_t non_quote_outside_string(uint64_t mask) const noexcept { return _string.non_quote_outside_string(mask); } - // string and escape characters - json_string_block _string; - // whitespace, structural characters ('operators'), scalars - json_character_block _characters; - // whether the previous character was a scalar - uint64_t _follows_potential_nonquote_scalar; - private: - // Potential structurals (i.e. disregarding strings) + // string and escape characters + json_string_block _string; + // whitespace, structural characters ('operators'), scalars + json_character_block _characters; + // whether the previous character was a scalar + uint64_t _follows_potential_nonquote_scalar; +private: + // Potential structurals (i.e. disregarding strings) - /** - * structural elements ([,],{,},:, comma) plus scalar starts like 123, true and "abc". - * They may reside inside a string. - **/ - simdjson_inline uint64_t potential_structural_start() const noexcept { return _characters.op() | potential_scalar_start(); } - /** - * The start of non-operator runs, like 123, true and "abc". - * It main reside inside a string. - **/ - simdjson_inline uint64_t potential_scalar_start() const noexcept { - // The term "scalar" refers to anything except structural characters and white space - // (so letters, numbers, quotes). - // Whenever it is preceded by something that is not a structural element ({,},[,],:, ") nor a white-space - // then we know that it is irrelevant structurally. - return _characters.scalar() & ~follows_potential_scalar(); - } - /** - * Whether the given character is immediately after a non-operator like 123, true. - * The characters following a quote are not included. - */ - simdjson_inline uint64_t follows_potential_scalar() const noexcept { - // _follows_potential_nonquote_scalar: is defined as marking any character that follows a character - // that is not a structural element ({,},[,],:, comma) nor a quote (") and that is not a - // white space. - // It is understood that within quoted region, anything at all could be marked (irrelevant). - return _follows_potential_nonquote_scalar; - } - }; + /** + * structural elements ([,],{,},:, comma) plus scalar starts like 123, true and "abc". + * They may reside inside a string. + **/ + simdjson_inline uint64_t potential_structural_start() const noexcept { return _characters.op() | potential_scalar_start(); } + /** + * The start of non-operator runs, like 123, true and "abc". + * It main reside inside a string. + **/ + simdjson_inline uint64_t potential_scalar_start() const noexcept { + // The term "scalar" refers to anything except structural characters and white space + // (so letters, numbers, quotes). + // Whenever it is preceded by something that is not a structural element ({,},[,],:, ") nor a white-space + // then we know that it is irrelevant structurally. + return _characters.scalar() & ~follows_potential_scalar(); + } + /** + * Whether the given character is immediately after a non-operator like 123, true. + * The characters following a quote are not included. + */ + simdjson_inline uint64_t follows_potential_scalar() const noexcept { + // _follows_potential_nonquote_scalar: is defined as marking any character that follows a character + // that is not a structural element ({,},[,],:, comma) nor a quote (") and that is not a + // white space. + // It is understood that within quoted region, anything at all could be marked (irrelevant). + return _follows_potential_nonquote_scalar; + } +}; - /** - * Scans JSON for important bits: structural characters or 'operators', strings, and scalars. - * - * The scanner starts by calculating two distinct things: - * - string characters (taking \" into account) - * - structural characters or 'operators' ([]{},:, comma) - * and scalars (runs of non-operators like 123, true and "abc") - * - * To minimize data dependency (a key component of the scanner's speed), it finds these in parallel: - * in particular, the operator/scalar bit will find plenty of things that are actually part of - * strings. When we're done, json_block will fuse the two together by masking out tokens that are - * part of a string. - */ - class json_scanner { - public: - json_scanner() = default; - simdjson_inline json_block next(const simd::simd8x64& in); - // Returns either UNCLOSED_STRING or SUCCESS - simdjson_inline error_code finish(); +/** + * Scans JSON for important bits: structural characters or 'operators', strings, and scalars. + * + * The scanner starts by calculating two distinct things: + * - string characters (taking \" into account) + * - structural characters or 'operators' ([]{},:, comma) + * and scalars (runs of non-operators like 123, true and "abc") + * + * To minimize data dependency (a key component of the scanner's speed), it finds these in parallel: + * in particular, the operator/scalar bit will find plenty of things that are actually part of + * strings. When we're done, json_block will fuse the two together by masking out tokens that are + * part of a string. + */ +class json_scanner { +public: + json_scanner() = default; + simdjson_inline json_block next(const simd::simd8x64& in); + // Returns either UNCLOSED_STRING or SUCCESS + simdjson_warn_unused simdjson_inline error_code finish(); - private: - // Whether the last character of the previous iteration is part of a scalar token - // (anything except whitespace or a structural character/'operator'). - uint64_t prev_scalar = 0ULL; - json_string_scanner string_scanner{}; - }; +private: + // Whether the last character of the previous iteration is part of a scalar token + // (anything except whitespace or a structural character/'operator'). + uint64_t prev_scalar = 0ULL; + json_string_scanner string_scanner{}; +}; - // - // Check if the current character immediately follows a matching character. - // - // For example, this checks for quotes with backslashes in front of them: - // - // const uint64_t backslashed_quote = in.eq('"') & immediately_follows(in.eq('\'), prev_backslash); - // - simdjson_inline uint64_t follows(const uint64_t match, uint64_t& overflow) { - const uint64_t result = match << 1 | overflow; - overflow = match >> 63; - return result; - } +// +// Check if the current character immediately follows a matching character. +// +// For example, this checks for quotes with backslashes in front of them: +// +// const uint64_t backslashed_quote = in.eq('"') & immediately_follows(in.eq('\'), prev_backslash); +// +simdjson_inline uint64_t follows(const uint64_t match, uint64_t &overflow) { + const uint64_t result = match << 1 | overflow; + overflow = match >> 63; + return result; +} - simdjson_inline json_block json_scanner::next(const simd::simd8x64& in) { - json_string_block strings = string_scanner.next(in); - // identifies the white-space and the structural characters - json_character_block characters = json_character_block::classify(in); - // The term "scalar" refers to anything except structural characters and white space - // (so letters, numbers, quotes). - // We want follows_scalar to mark anything that follows a non-quote scalar (so letters and numbers). - // - // A terminal quote should either be followed by a structural character (comma, brace, bracket, colon) - // or nothing. However, we still want ' "a string"true ' to mark the 't' of 'true' as a potential - // pseudo-structural character just like we would if we had ' "a string" true '; otherwise we - // may need to add an extra check when parsing strings. - // - // Performance: there are many ways to skin this cat. - const uint64_t nonquote_scalar = characters.scalar() & ~strings.quote(); - uint64_t follows_nonquote_scalar = follows(nonquote_scalar, prev_scalar); - // We are returning a function-local object so either we get a move constructor - // or we get copy elision. - return json_block( - strings,// strings is a function-local object so either it moves or the copy is elided. - characters, - follows_nonquote_scalar - ); - } +simdjson_inline json_block json_scanner::next(const simd::simd8x64& in) { + json_string_block strings = string_scanner.next(in); + // identifies the white-space and the structural characters + json_character_block characters = json_character_block::classify(in); + // The term "scalar" refers to anything except structural characters and white space + // (so letters, numbers, quotes). + // We want follows_scalar to mark anything that follows a non-quote scalar (so letters and numbers). + // + // A terminal quote should either be followed by a structural character (comma, brace, bracket, colon) + // or nothing. However, we still want ' "a string"true ' to mark the 't' of 'true' as a potential + // pseudo-structural character just like we would if we had ' "a string" true '; otherwise we + // may need to add an extra check when parsing strings. + // + // Performance: there are many ways to skin this cat. + const uint64_t nonquote_scalar = characters.scalar() & ~strings.quote(); + uint64_t follows_nonquote_scalar = follows(nonquote_scalar, prev_scalar); + // We are returning a function-local object so either we get a move constructor + // or we get copy elision. + return json_block( + strings,// strings is a function-local object so either it moves or the copy is elided. + characters, + follows_nonquote_scalar + ); +} - simdjson_inline error_code json_scanner::finish() { - return string_scanner.finish(); - } +simdjson_warn_unused simdjson_inline error_code json_scanner::finish() { + return string_scanner.finish(); +} - } // namespace stage1 - } // unnamed namespace - } // namespace lsx +} // namespace stage1 +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_SCANNER_H @@ -44287,99 +52364,99 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace { - namespace stage1 { +namespace lsx { +namespace { +namespace stage1 { - /** - * This algorithm is used to quickly identify the last structural position that - * makes up a complete document. - * - * It does this by going backwards and finding the last *document boundary* (a - * place where one value follows another without a comma between them). If the - * last document (the characters after the boundary) has an equal number of - * start and end brackets, it is considered complete. - * - * Simply put, we iterate over the structural characters, starting from - * the end. We consider that we found the end of a JSON document when the - * first element of the pair is NOT one of these characters: '{' '[' ':' ',' - * and when the second element is NOT one of these characters: '}' ']' ':' ','. - * - * This simple comparison works most of the time, but it does not cover cases - * where the batch's structural indexes contain a perfect amount of documents. - * In such a case, we do not have access to the structural index which follows - * the last document, therefore, we do not have access to the second element in - * the pair, and that means we cannot identify the last document. To fix this - * issue, we keep a count of the open and closed curly/square braces we found - * while searching for the pair. When we find a pair AND the count of open and - * closed curly/square braces is the same, we know that we just passed a - * complete document, therefore the last json buffer location is the end of the - * batch. - */ - simdjson_inline uint32_t find_next_document_index(dom_parser_implementation& parser) { - // Variant: do not count separately, just figure out depth - if (parser.n_structural_indexes == 0) { return 0; } - auto arr_cnt = 0; - auto obj_cnt = 0; - for (auto i = parser.n_structural_indexes - 1; i > 0; i--) { - auto idxb = parser.structural_indexes[i]; - switch (parser.buf[idxb]) { - case ':': - case ',': - continue; - case '}': - obj_cnt--; - continue; - case ']': - arr_cnt--; - continue; - case '{': - obj_cnt++; - break; - case '[': - arr_cnt++; - break; - } - auto idxa = parser.structural_indexes[i - 1]; - switch (parser.buf[idxa]) { - case '{': - case '[': - case ':': - case ',': - continue; - } - // Last document is complete, so the next document will appear after! - if (!arr_cnt && !obj_cnt) { - return parser.n_structural_indexes; - } - // Last document is incomplete; mark the document at i + 1 as the next one - return i; - } - // If we made it to the end, we want to finish counting to see if we have a full document. - switch (parser.buf[parser.structural_indexes[0]]) { - case '}': - obj_cnt--; - break; - case ']': - arr_cnt--; - break; - case '{': - obj_cnt++; - break; - case '[': - arr_cnt++; - break; - } - if (!arr_cnt && !obj_cnt) { - // We have a complete document. - return parser.n_structural_indexes; - } - return 0; - } +/** + * This algorithm is used to quickly identify the last structural position that + * makes up a complete document. + * + * It does this by going backwards and finding the last *document boundary* (a + * place where one value follows another without a comma between them). If the + * last document (the characters after the boundary) has an equal number of + * start and end brackets, it is considered complete. + * + * Simply put, we iterate over the structural characters, starting from + * the end. We consider that we found the end of a JSON document when the + * first element of the pair is NOT one of these characters: '{' '[' ':' ',' + * and when the second element is NOT one of these characters: '}' ']' ':' ','. + * + * This simple comparison works most of the time, but it does not cover cases + * where the batch's structural indexes contain a perfect amount of documents. + * In such a case, we do not have access to the structural index which follows + * the last document, therefore, we do not have access to the second element in + * the pair, and that means we cannot identify the last document. To fix this + * issue, we keep a count of the open and closed curly/square braces we found + * while searching for the pair. When we find a pair AND the count of open and + * closed curly/square braces is the same, we know that we just passed a + * complete document, therefore the last json buffer location is the end of the + * batch. + */ +simdjson_inline uint32_t find_next_document_index(dom_parser_implementation &parser) { + // Variant: do not count separately, just figure out depth + if(parser.n_structural_indexes == 0) { return 0; } + auto arr_cnt = 0; + auto obj_cnt = 0; + for (auto i = parser.n_structural_indexes - 1; i > 0; i--) { + auto idxb = parser.structural_indexes[i]; + switch (parser.buf[idxb]) { + case ':': + case ',': + continue; + case '}': + obj_cnt--; + continue; + case ']': + arr_cnt--; + continue; + case '{': + obj_cnt++; + break; + case '[': + arr_cnt++; + break; + } + auto idxa = parser.structural_indexes[i - 1]; + switch (parser.buf[idxa]) { + case '{': + case '[': + case ':': + case ',': + continue; + } + // Last document is complete, so the next document will appear after! + if (!arr_cnt && !obj_cnt) { + return parser.n_structural_indexes; + } + // Last document is incomplete; mark the document at i + 1 as the next one + return i; + } + // If we made it to the end, we want to finish counting to see if we have a full document. + switch (parser.buf[parser.structural_indexes[0]]) { + case '}': + obj_cnt--; + break; + case ']': + arr_cnt--; + break; + case '{': + obj_cnt++; + break; + case '[': + arr_cnt++; + break; + } + if (!arr_cnt && !obj_cnt) { + // We have a complete document. + return parser.n_structural_indexes; + } + return 0; +} - } // namespace stage1 - } // unnamed namespace - } // namespace lsx +} // namespace stage1 +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_FIND_NEXT_DOCUMENT_INDEX_H @@ -44401,92 +52478,92 @@ namespace simdjson { // "simdjson/stage1.h" (this simplifies amalgation) namespace simdjson { - namespace lsx { - namespace { - namespace stage1 { +namespace lsx { +namespace { +namespace stage1 { - class json_minifier { - public: - template - static error_code minify(const uint8_t* buf, size_t len, uint8_t* dst, size_t& dst_len) noexcept; +class json_minifier { +public: + template + static error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) noexcept; - private: - simdjson_inline json_minifier(uint8_t* _dst) - : dst{ _dst } - {} - template - simdjson_inline void step(const uint8_t* block_buf, buf_block_reader& reader) noexcept; - simdjson_inline void next(const simd::simd8x64& in, const json_block& block); - simdjson_inline error_code finish(uint8_t* dst_start, size_t& dst_len); - json_scanner scanner{}; - uint8_t* dst; - }; +private: + simdjson_inline json_minifier(uint8_t *_dst) + : dst{_dst} + {} + template + simdjson_inline void step(const uint8_t *block_buf, buf_block_reader &reader) noexcept; + simdjson_inline void next(const simd::simd8x64& in, const json_block& block); + simdjson_warn_unused simdjson_inline error_code finish(uint8_t *dst_start, size_t &dst_len); + json_scanner scanner{}; + uint8_t *dst; +}; - simdjson_inline void json_minifier::next(const simd::simd8x64& in, const json_block& block) { - uint64_t mask = block.whitespace(); - dst += in.compress(mask, dst); - } +simdjson_inline void json_minifier::next(const simd::simd8x64& in, const json_block& block) { + uint64_t mask = block.whitespace(); + dst += in.compress(mask, dst); +} - simdjson_inline error_code json_minifier::finish(uint8_t* dst_start, size_t& dst_len) { - error_code error = scanner.finish(); - if (error) { dst_len = 0; return error; } - dst_len = dst - dst_start; - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code json_minifier::finish(uint8_t *dst_start, size_t &dst_len) { + error_code error = scanner.finish(); + if (error) { dst_len = 0; return error; } + dst_len = dst - dst_start; + return SUCCESS; +} - template<> - simdjson_inline void json_minifier::step<128>(const uint8_t* block_buf, buf_block_reader<128>& reader) noexcept { - simd::simd8x64 in_1(block_buf); - simd::simd8x64 in_2(block_buf + 64); - json_block block_1 = scanner.next(in_1); - json_block block_2 = scanner.next(in_2); - this->next(in_1, block_1); - this->next(in_2, block_2); - reader.advance(); - } +template<> +simdjson_inline void json_minifier::step<128>(const uint8_t *block_buf, buf_block_reader<128> &reader) noexcept { + simd::simd8x64 in_1(block_buf); + simd::simd8x64 in_2(block_buf+64); + json_block block_1 = scanner.next(in_1); + json_block block_2 = scanner.next(in_2); + this->next(in_1, block_1); + this->next(in_2, block_2); + reader.advance(); +} - template<> - simdjson_inline void json_minifier::step<64>(const uint8_t* block_buf, buf_block_reader<64>& reader) noexcept { - simd::simd8x64 in_1(block_buf); - json_block block_1 = scanner.next(in_1); - this->next(block_buf, block_1); - reader.advance(); - } +template<> +simdjson_inline void json_minifier::step<64>(const uint8_t *block_buf, buf_block_reader<64> &reader) noexcept { + simd::simd8x64 in_1(block_buf); + json_block block_1 = scanner.next(in_1); + this->next(block_buf, block_1); + reader.advance(); +} - template - error_code json_minifier::minify(const uint8_t* buf, size_t len, uint8_t* dst, size_t& dst_len) noexcept { - buf_block_reader reader(buf, len); - json_minifier minifier(dst); +template +error_code json_minifier::minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) noexcept { + buf_block_reader reader(buf, len); + json_minifier minifier(dst); - // Index the first n-1 blocks - while (reader.has_full_block()) { - minifier.step(reader.full_block(), reader); - } + // Index the first n-1 blocks + while (reader.has_full_block()) { + minifier.step(reader.full_block(), reader); + } - // Index the last (remainder) block, padded with spaces - uint8_t block[STEP_SIZE]; - size_t remaining_bytes = reader.get_remainder(block); - if (remaining_bytes > 0) { - // We do not want to write directly to the output stream. Rather, we write - // to a local buffer (for safety). - uint8_t out_block[STEP_SIZE]; - uint8_t* const guarded_dst{ minifier.dst }; - minifier.dst = out_block; - minifier.step(block, reader); - size_t to_write = minifier.dst - out_block; - // In some cases, we could be enticed to consider the padded spaces - // as part of the string. This is fine as long as we do not write more - // than we consumed. - if (to_write > remaining_bytes) { to_write = remaining_bytes; } - memcpy(guarded_dst, out_block, to_write); - minifier.dst = guarded_dst + to_write; - } - return minifier.finish(dst, dst_len); - } + // Index the last (remainder) block, padded with spaces + uint8_t block[STEP_SIZE]; + size_t remaining_bytes = reader.get_remainder(block); + if (remaining_bytes > 0) { + // We do not want to write directly to the output stream. Rather, we write + // to a local buffer (for safety). + uint8_t out_block[STEP_SIZE]; + uint8_t * const guarded_dst{minifier.dst}; + minifier.dst = out_block; + minifier.step(block, reader); + size_t to_write = minifier.dst - out_block; + // In some cases, we could be enticed to consider the padded spaces + // as part of the string. This is fine as long as we do not write more + // than we consumed. + if(to_write > remaining_bytes) { to_write = remaining_bytes; } + memcpy(guarded_dst, out_block, to_write); + minifier.dst = guarded_dst + to_write; + } + return minifier.finish(dst, dst_len); +} - } // namespace stage1 - } // unnamed namespace - } // namespace lsx +} // namespace stage1 +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_MINIFIER_H @@ -44512,341 +52589,339 @@ namespace simdjson { // "simdjson/stage1.h" (this simplifies amalgation) namespace simdjson { - namespace lsx { - namespace { - namespace stage1 { +namespace lsx { +namespace { +namespace stage1 { - class bit_indexer { - public: - uint32_t* tail; +class bit_indexer { +public: + uint32_t *tail; - simdjson_inline bit_indexer(uint32_t* index_buf) : tail(index_buf) {} + simdjson_inline bit_indexer(uint32_t *index_buf) : tail(index_buf) {} #if SIMDJSON_PREFER_REVERSE_BITS - /** - * ARM lacks a fast trailing zero instruction, but it has a fast - * bit reversal instruction and a fast leading zero instruction. - * Thus it may be profitable to reverse the bits (once) and then - * to rely on a sequence of instructions that call the leading - * zero instruction. - * - * Performance notes: - * The chosen routine is not optimal in terms of data dependency - * since zero_leading_bit might require two instructions. However, - * it tends to minimize the total number of instructions which is - * beneficial. - */ - simdjson_inline void write_index(uint32_t idx, uint64_t& rev_bits, int i) { - int lz = leading_zeroes(rev_bits); - this->tail[i] = static_cast(idx) + lz; - rev_bits = zero_leading_bit(rev_bits, lz); - } + /** + * ARM lacks a fast trailing zero instruction, but it has a fast + * bit reversal instruction and a fast leading zero instruction. + * Thus it may be profitable to reverse the bits (once) and then + * to rely on a sequence of instructions that call the leading + * zero instruction. + * + * Performance notes: + * The chosen routine is not optimal in terms of data dependency + * since zero_leading_bit might require two instructions. However, + * it tends to minimize the total number of instructions which is + * beneficial. + */ + simdjson_inline void write_index(uint32_t idx, uint64_t& rev_bits, int i) { + int lz = leading_zeroes(rev_bits); + this->tail[i] = static_cast(idx) + lz; + rev_bits = zero_leading_bit(rev_bits, lz); + } #else - /** - * Under recent x64 systems, we often have both a fast trailing zero - * instruction and a fast 'clear-lower-bit' instruction so the following - * algorithm can be competitive. - */ + /** + * Under recent x64 systems, we often have both a fast trailing zero + * instruction and a fast 'clear-lower-bit' instruction so the following + * algorithm can be competitive. + */ - simdjson_inline void write_index(uint32_t idx, uint64_t& bits, int i) { - this->tail[i] = idx + trailing_zeroes(bits); - bits = clear_lowest_bit(bits); - } + simdjson_inline void write_index(uint32_t idx, uint64_t& bits, int i) { + this->tail[i] = idx + trailing_zeroes(bits); + bits = clear_lowest_bit(bits); + } #endif // SIMDJSON_PREFER_REVERSE_BITS - template - simdjson_inline int write_indexes(uint32_t idx, uint64_t& bits) { - write_index(idx, bits, START); - SIMDJSON_IF_CONSTEXPR(N > 1) { - write_indexes<(N - 1 > 0 ? START + 1 : START), (N - 1 >= 0 ? N - 1 : 1)>(idx, bits); - } - return START + N; - } + template + simdjson_inline int write_indexes(uint32_t idx, uint64_t& bits) { + write_index(idx, bits, START); + SIMDJSON_IF_CONSTEXPR (N > 1) { + write_indexes<(N-1>0?START+1:START), (N-1>=0?N-1:1)>(idx, bits); + } + return START+N; + } - template - simdjson_inline int write_indexes_stepped(uint32_t idx, uint64_t& bits, int cnt) { - write_indexes(idx, bits); - SIMDJSON_IF_CONSTEXPR((START + STEP) < END) { - if (simdjson_unlikely((START + STEP) < cnt)) { - write_indexes_stepped<(START + STEP < END ? START + STEP : END), END, STEP>(idx, bits, cnt); - } - } - return ((END - START) % STEP) == 0 ? END : (END - START) - ((END - START) % STEP) + STEP; - } + template + simdjson_inline int write_indexes_stepped(uint32_t idx, uint64_t& bits, int cnt) { + write_indexes(idx, bits); + SIMDJSON_IF_CONSTEXPR ((START+STEP) < END) { + if (simdjson_unlikely((START+STEP) < cnt)) { + write_indexes_stepped<(START+STEP(idx, bits, cnt); + } + } + return ((END-START) % STEP) == 0 ? END : (END-START) - ((END-START) % STEP) + STEP; + } - // flatten out values in 'bits' assuming that they are are to have values of idx - // plus their position in the bitvector, and store these indexes at - // base_ptr[base] incrementing base as we go - // will potentially store extra values beyond end of valid bits, so base_ptr - // needs to be large enough to handle this - // - // If the kernel sets SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER, then it - // will provide its own version of the code. + // flatten out values in 'bits' assuming that they are are to have values of idx + // plus their position in the bitvector, and store these indexes at + // base_ptr[base] incrementing base as we go + // will potentially store extra values beyond end of valid bits, so base_ptr + // needs to be large enough to handle this + // + // If the kernel sets SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER, then it + // will provide its own version of the code. #ifdef SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER - simdjson_inline void write(uint32_t idx, uint64_t bits); + simdjson_inline void write(uint32_t idx, uint64_t bits); #else - simdjson_inline void write(uint32_t idx, uint64_t bits) { - // In some instances, the next branch is expensive because it is mispredicted. - // Unfortunately, in other cases, - // it helps tremendously. - if (bits == 0) - return; + simdjson_inline void write(uint32_t idx, uint64_t bits) { + // In some instances, the next branch is expensive because it is mispredicted. + // Unfortunately, in other cases, + // it helps tremendously. + if (bits == 0) + return; - int cnt = static_cast(count_ones(bits)); + int cnt = static_cast(count_ones(bits)); #if SIMDJSON_PREFER_REVERSE_BITS - bits = reverse_bits(bits); + bits = reverse_bits(bits); #endif #ifdef SIMDJSON_STRUCTURAL_INDEXER_STEP - static constexpr const int STEP = SIMDJSON_STRUCTURAL_INDEXER_STEP; + static constexpr const int STEP = SIMDJSON_STRUCTURAL_INDEXER_STEP; #else - static constexpr const int STEP = 4; + static constexpr const int STEP = 4; #endif - static constexpr const int STEP_UNTIL = 24; + static constexpr const int STEP_UNTIL = 24; - write_indexes_stepped<0, STEP_UNTIL, STEP>(idx, bits, cnt); - SIMDJSON_IF_CONSTEXPR(STEP_UNTIL < 64) { - if (simdjson_unlikely(STEP_UNTIL < cnt)) { - for (int i = STEP_UNTIL; i < cnt; i++) { - write_index(idx, bits, i); - } - } - } + write_indexes_stepped<0, STEP_UNTIL, STEP>(idx, bits, cnt); + SIMDJSON_IF_CONSTEXPR (STEP_UNTIL < 64) { + if (simdjson_unlikely(STEP_UNTIL < cnt)) { + for (int i=STEP_UNTIL; itail += cnt; - } + this->tail += cnt; + } #endif // SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER - }; +}; - class json_structural_indexer { - public: - /** - * Find the important bits of JSON in a 128-byte chunk, and add them to structural_indexes. - * - * @param partial Setting the partial parameter to true allows the find_structural_bits to - * tolerate unclosed strings. The caller should still ensure that the input is valid UTF-8. If - * you are processing substrings, you may want to call on a function like trimmed_length_safe_utf8. - */ - template - static error_code index(const uint8_t* buf, size_t len, dom_parser_implementation& parser, stage1_mode partial) noexcept; +class json_structural_indexer { +public: + /** + * Find the important bits of JSON in a 128-byte chunk, and add them to structural_indexes. + * + * @param partial Setting the partial parameter to true allows the find_structural_bits to + * tolerate unclosed strings. The caller should still ensure that the input is valid UTF-8. If + * you are processing substrings, you may want to call on a function like trimmed_length_safe_utf8. + */ + template + static error_code index(const uint8_t *buf, size_t len, dom_parser_implementation &parser, stage1_mode partial) noexcept; - private: - simdjson_inline json_structural_indexer(uint32_t* structural_indexes); - template - simdjson_inline void step(const uint8_t* block, buf_block_reader& reader) noexcept; - simdjson_inline void next(const simd::simd8x64& in, const json_block& block, size_t idx); - simdjson_inline error_code finish(dom_parser_implementation& parser, size_t idx, size_t len, stage1_mode partial); +private: + simdjson_inline json_structural_indexer(uint32_t *structural_indexes); + template + simdjson_inline void step(const uint8_t *block, buf_block_reader &reader) noexcept; + simdjson_inline void next(const simd::simd8x64& in, const json_block& block, size_t idx); + simdjson_warn_unused simdjson_inline error_code finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial); - json_scanner scanner{}; - utf8_checker checker{}; - bit_indexer indexer; - uint64_t prev_structurals = 0; - uint64_t unescaped_chars_error = 0; - }; + json_scanner scanner{}; + utf8_checker checker{}; + bit_indexer indexer; + uint64_t prev_structurals = 0; + uint64_t unescaped_chars_error = 0; +}; - simdjson_inline json_structural_indexer::json_structural_indexer(uint32_t* structural_indexes) : indexer{ structural_indexes } {} +simdjson_inline json_structural_indexer::json_structural_indexer(uint32_t *structural_indexes) : indexer{structural_indexes} {} - // Skip the last character if it is partial - simdjson_inline size_t trim_partial_utf8(const uint8_t* buf, size_t len) { - if (simdjson_unlikely(len < 3)) { - switch (len) { - case 2: - if (buf[len - 1] >= 0xc0) { return len - 1; } // 2-, 3- and 4-byte characters with only 1 byte left - if (buf[len - 2] >= 0xe0) { return len - 2; } // 3- and 4-byte characters with only 2 bytes left - return len; - case 1: - if (buf[len - 1] >= 0xc0) { return len - 1; } // 2-, 3- and 4-byte characters with only 1 byte left - return len; - case 0: - return len; - } - } - if (buf[len - 1] >= 0xc0) { return len - 1; } // 2-, 3- and 4-byte characters with only 1 byte left - if (buf[len - 2] >= 0xe0) { return len - 2; } // 3- and 4-byte characters with only 1 byte left - if (buf[len - 3] >= 0xf0) { return len - 3; } // 4-byte characters with only 3 bytes left - return len; - } +// Skip the last character if it is partial +simdjson_inline size_t trim_partial_utf8(const uint8_t *buf, size_t len) { + if (simdjson_unlikely(len < 3)) { + switch (len) { + case 2: + if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left + if (buf[len-2] >= 0xe0) { return len-2; } // 3- and 4-byte characters with only 2 bytes left + return len; + case 1: + if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left + return len; + case 0: + return len; + } + } + if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left + if (buf[len-2] >= 0xe0) { return len-2; } // 3- and 4-byte characters with only 1 byte left + if (buf[len-3] >= 0xf0) { return len-3; } // 4-byte characters with only 3 bytes left + return len; +} - // - // PERF NOTES: - // We pipe 2 inputs through these stages: - // 1. Load JSON into registers. This takes a long time and is highly parallelizable, so we load - // 2 inputs' worth at once so that by the time step 2 is looking for them input, it's available. - // 2. Scan the JSON for critical data: strings, scalars and operators. This is the critical path. - // The output of step 1 depends entirely on this information. These functions don't quite use - // up enough CPU: the second half of the functions is highly serial, only using 1 execution core - // at a time. The second input's scans has some dependency on the first ones finishing it, but - // they can make a lot of progress before they need that information. - // 3. Step 1 does not use enough capacity, so we run some extra stuff while we're waiting for that - // to finish: utf-8 checks and generating the output from the last iteration. - // - // The reason we run 2 inputs at a time, is steps 2 and 3 are *still* not enough to soak up all - // available capacity with just one input. Running 2 at a time seems to give the CPU a good enough - // workout. - // - template - error_code json_structural_indexer::index(const uint8_t* buf, size_t len, dom_parser_implementation& parser, stage1_mode partial) noexcept { - if (simdjson_unlikely(len > parser.capacity())) { return CAPACITY; } - // We guard the rest of the code so that we can assume that len > 0 throughout. - if (len == 0) { return EMPTY; } - if (is_streaming(partial)) { - len = trim_partial_utf8(buf, len); - // If you end up with an empty window after trimming - // the partial UTF-8 bytes, then chances are good that you - // have an UTF-8 formatting error. - if (len == 0) { return UTF8_ERROR; } - } - buf_block_reader reader(buf, len); - json_structural_indexer indexer(parser.structural_indexes.get()); +// +// PERF NOTES: +// We pipe 2 inputs through these stages: +// 1. Load JSON into registers. This takes a long time and is highly parallelizable, so we load +// 2 inputs' worth at once so that by the time step 2 is looking for them input, it's available. +// 2. Scan the JSON for critical data: strings, scalars and operators. This is the critical path. +// The output of step 1 depends entirely on this information. These functions don't quite use +// up enough CPU: the second half of the functions is highly serial, only using 1 execution core +// at a time. The second input's scans has some dependency on the first ones finishing it, but +// they can make a lot of progress before they need that information. +// 3. Step 1 does not use enough capacity, so we run some extra stuff while we're waiting for that +// to finish: utf-8 checks and generating the output from the last iteration. +// +// The reason we run 2 inputs at a time, is steps 2 and 3 are *still* not enough to soak up all +// available capacity with just one input. Running 2 at a time seems to give the CPU a good enough +// workout. +// +template +error_code json_structural_indexer::index(const uint8_t *buf, size_t len, dom_parser_implementation &parser, stage1_mode partial) noexcept { + if (simdjson_unlikely(len > parser.capacity())) { return CAPACITY; } + // We guard the rest of the code so that we can assume that len > 0 throughout. + if (len == 0) { return EMPTY; } + if (is_streaming(partial)) { + len = trim_partial_utf8(buf, len); + // If you end up with an empty window after trimming + // the partial UTF-8 bytes, then chances are good that you + // have an UTF-8 formatting error. + if(len == 0) { return UTF8_ERROR; } + } + buf_block_reader reader(buf, len); + json_structural_indexer indexer(parser.structural_indexes.get()); - // Read all but the last block - while (reader.has_full_block()) { - indexer.step(reader.full_block(), reader); - } - // Take care of the last block (will always be there unless file is empty which is - // not supposed to happen.) - uint8_t block[STEP_SIZE]; - if (simdjson_unlikely(reader.get_remainder(block) == 0)) { return UNEXPECTED_ERROR; } - indexer.step(block, reader); - return indexer.finish(parser, reader.block_index(), len, partial); - } + // Read all but the last block + while (reader.has_full_block()) { + indexer.step(reader.full_block(), reader); + } + // Take care of the last block (will always be there unless file is empty which is + // not supposed to happen.) + uint8_t block[STEP_SIZE]; + if (simdjson_unlikely(reader.get_remainder(block) == 0)) { return UNEXPECTED_ERROR; } + indexer.step(block, reader); + return indexer.finish(parser, reader.block_index(), len, partial); +} - template<> - simdjson_inline void json_structural_indexer::step<128>(const uint8_t* block, buf_block_reader<128>& reader) noexcept { - simd::simd8x64 in_1(block); - simd::simd8x64 in_2(block + 64); - json_block block_1 = scanner.next(in_1); - json_block block_2 = scanner.next(in_2); - this->next(in_1, block_1, reader.block_index()); - this->next(in_2, block_2, reader.block_index() + 64); - reader.advance(); - } +template<> +simdjson_inline void json_structural_indexer::step<128>(const uint8_t *block, buf_block_reader<128> &reader) noexcept { + simd::simd8x64 in_1(block); + simd::simd8x64 in_2(block+64); + json_block block_1 = scanner.next(in_1); + json_block block_2 = scanner.next(in_2); + this->next(in_1, block_1, reader.block_index()); + this->next(in_2, block_2, reader.block_index()+64); + reader.advance(); +} - template<> - simdjson_inline void json_structural_indexer::step<64>(const uint8_t* block, buf_block_reader<64>& reader) noexcept { - simd::simd8x64 in_1(block); - json_block block_1 = scanner.next(in_1); - this->next(in_1, block_1, reader.block_index()); - reader.advance(); - } +template<> +simdjson_inline void json_structural_indexer::step<64>(const uint8_t *block, buf_block_reader<64> &reader) noexcept { + simd::simd8x64 in_1(block); + json_block block_1 = scanner.next(in_1); + this->next(in_1, block_1, reader.block_index()); + reader.advance(); +} - simdjson_inline void json_structural_indexer::next(const simd::simd8x64& in, const json_block& block, size_t idx) { - uint64_t unescaped = in.lteq(0x1F); +simdjson_inline void json_structural_indexer::next(const simd::simd8x64& in, const json_block& block, size_t idx) { + uint64_t unescaped = in.lteq(0x1F); #if SIMDJSON_UTF8VALIDATION - checker.check_next_input(in); + checker.check_next_input(in); #endif - indexer.write(uint32_t(idx - 64), prev_structurals); // Output *last* iteration's structurals to the parser - prev_structurals = block.structural_start(); - unescaped_chars_error |= block.non_quote_inside_string(unescaped); - } + indexer.write(uint32_t(idx-64), prev_structurals); // Output *last* iteration's structurals to the parser + prev_structurals = block.structural_start(); + unescaped_chars_error |= block.non_quote_inside_string(unescaped); +} - simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementation& parser, size_t idx, size_t len, stage1_mode partial) { - // Write out the final iteration's structurals - indexer.write(uint32_t(idx - 64), prev_structurals); - error_code error = scanner.finish(); - // We deliberately break down the next expression so that it is - // human readable. - const bool should_we_exit = is_streaming(partial) ? - ((error != SUCCESS) && (error != UNCLOSED_STRING)) // when partial we tolerate UNCLOSED_STRING - : (error != SUCCESS); // if partial is false, we must have SUCCESS - const bool have_unclosed_string = (error == UNCLOSED_STRING); - if (simdjson_unlikely(should_we_exit)) { return error; } +simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial) { + // Write out the final iteration's structurals + indexer.write(uint32_t(idx-64), prev_structurals); + error_code error = scanner.finish(); + // We deliberately break down the next expression so that it is + // human readable. + const bool should_we_exit = is_streaming(partial) ? + ((error != SUCCESS) && (error != UNCLOSED_STRING)) // when partial we tolerate UNCLOSED_STRING + : (error != SUCCESS); // if partial is false, we must have SUCCESS + const bool have_unclosed_string = (error == UNCLOSED_STRING); + if (simdjson_unlikely(should_we_exit)) { return error; } - if (unescaped_chars_error) { - return UNESCAPED_CHARS; - } - parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get()); - /*** - * The On-Demand API requires special padding. - * - * This is related to https://github.com/simdjson/simdjson/issues/906 - * Basically, we want to make sure that if the parsing continues beyond the last (valid) - * structural character, it quickly stops. - * Only three structural characters can be repeated without triggering an error in JSON: [,] and }. - * We repeat the padding character (at 'len'). We don't know what it is, but if the parsing - * continues, then it must be [,] or }. - * Suppose it is ] or }. We backtrack to the first character, what could it be that would - * not trigger an error? It could be ] or } but no, because you can't start a document that way. - * It can't be a comma, a colon or any simple value. So the only way we could continue is - * if the repeated character is [. But if so, the document must start with [. But if the document - * starts with [, it should end with ]. If we enforce that rule, then we would get - * ][[ which is invalid. - * - * This is illustrated with the test array_iterate_unclosed_error() on the following input: - * R"({ "a": [,,)" - **/ - parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); // used later in partial == stage1_mode::streaming_final - parser.structural_indexes[parser.n_structural_indexes + 1] = uint32_t(len); - parser.structural_indexes[parser.n_structural_indexes + 2] = 0; - parser.next_structural_index = 0; - // a valid JSON file cannot have zero structural indexes - we should have found something - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { - return EMPTY; - } - if (simdjson_unlikely(parser.structural_indexes[parser.n_structural_indexes - 1] > len)) { - return UNEXPECTED_ERROR; - } - if (partial == stage1_mode::streaming_partial) { - // If we have an unclosed string, then the last structural - // will be the quote and we want to make sure to omit it. - if (have_unclosed_string) { - parser.n_structural_indexes--; - // a valid JSON file cannot have zero structural indexes - we should have found something - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; } - } - // We truncate the input to the end of the last complete document (or zero). - auto new_structural_indexes = find_next_document_index(parser); - if (new_structural_indexes == 0 && parser.n_structural_indexes > 0) { - if (parser.structural_indexes[0] == 0) { - // If the buffer is partial and we started at index 0 but the document is - // incomplete, it's too big to parse. - return CAPACITY; - } - else { - // It is possible that the document could be parsed, we just had a lot - // of white space. - parser.n_structural_indexes = 0; - return EMPTY; - } - } + if (unescaped_chars_error) { + return UNESCAPED_CHARS; + } + parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get()); + /*** + * The On-Demand API requires special padding. + * + * This is related to https://github.com/simdjson/simdjson/issues/906 + * Basically, we want to make sure that if the parsing continues beyond the last (valid) + * structural character, it quickly stops. + * Only three structural characters can be repeated without triggering an error in JSON: [,] and }. + * We repeat the padding character (at 'len'). We don't know what it is, but if the parsing + * continues, then it must be [,] or }. + * Suppose it is ] or }. We backtrack to the first character, what could it be that would + * not trigger an error? It could be ] or } but no, because you can't start a document that way. + * It can't be a comma, a colon or any simple value. So the only way we could continue is + * if the repeated character is [. But if so, the document must start with [. But if the document + * starts with [, it should end with ]. If we enforce that rule, then we would get + * ][[ which is invalid. + * + * This is illustrated with the test array_iterate_unclosed_error() on the following input: + * R"({ "a": [,,)" + **/ + parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); // used later in partial == stage1_mode::streaming_final + parser.structural_indexes[parser.n_structural_indexes + 1] = uint32_t(len); + parser.structural_indexes[parser.n_structural_indexes + 2] = 0; + parser.next_structural_index = 0; + // a valid JSON file cannot have zero structural indexes - we should have found something + if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { + return EMPTY; + } + if (simdjson_unlikely(parser.structural_indexes[parser.n_structural_indexes - 1] > len)) { + return UNEXPECTED_ERROR; + } + if (partial == stage1_mode::streaming_partial) { + // If we have an unclosed string, then the last structural + // will be the quote and we want to make sure to omit it. + if(have_unclosed_string) { + parser.n_structural_indexes--; + // a valid JSON file cannot have zero structural indexes - we should have found something + if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; } + } + // We truncate the input to the end of the last complete document (or zero). + auto new_structural_indexes = find_next_document_index(parser); + if (new_structural_indexes == 0 && parser.n_structural_indexes > 0) { + if(parser.structural_indexes[0] == 0) { + // If the buffer is partial and we started at index 0 but the document is + // incomplete, it's too big to parse. + return CAPACITY; + } else { + // It is possible that the document could be parsed, we just had a lot + // of white space. + parser.n_structural_indexes = 0; + return EMPTY; + } + } - parser.n_structural_indexes = new_structural_indexes; - } - else if (partial == stage1_mode::streaming_final) { - if (have_unclosed_string) { parser.n_structural_indexes--; } - // We truncate the input to the end of the last complete document (or zero). - // Because partial == stage1_mode::streaming_final, it means that we may - // silently ignore trailing garbage. Though it sounds bad, we do it - // deliberately because many people who have streams of JSON documents - // will truncate them for processing. E.g., imagine that you are uncompressing - // the data from a size file or receiving it in chunks from the network. You - // may not know where exactly the last document will be. Meanwhile the - // document_stream instances allow people to know the JSON documents they are - // parsing (see the iterator.source() method). - parser.n_structural_indexes = find_next_document_index(parser); - // We store the initial n_structural_indexes so that the client can see - // whether we used truncation. If initial_n_structural_indexes == parser.n_structural_indexes, - // then this will query parser.structural_indexes[parser.n_structural_indexes] which is len, - // otherwise, it will copy some prior index. - parser.structural_indexes[parser.n_structural_indexes + 1] = parser.structural_indexes[parser.n_structural_indexes]; - // This next line is critical, do not change it unless you understand what you are - // doing. - parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { - // We tolerate an unclosed string at the very end of the stream. Indeed, users - // often load their data in bulk without being careful and they want us to ignore - // the trailing garbage. - return EMPTY; - } - } - checker.check_eof(); - return checker.errors(); - } + parser.n_structural_indexes = new_structural_indexes; + } else if (partial == stage1_mode::streaming_final) { + if(have_unclosed_string) { parser.n_structural_indexes--; } + // We truncate the input to the end of the last complete document (or zero). + // Because partial == stage1_mode::streaming_final, it means that we may + // silently ignore trailing garbage. Though it sounds bad, we do it + // deliberately because many people who have streams of JSON documents + // will truncate them for processing. E.g., imagine that you are uncompressing + // the data from a size file or receiving it in chunks from the network. You + // may not know where exactly the last document will be. Meanwhile the + // document_stream instances allow people to know the JSON documents they are + // parsing (see the iterator.source() method). + parser.n_structural_indexes = find_next_document_index(parser); + // We store the initial n_structural_indexes so that the client can see + // whether we used truncation. If initial_n_structural_indexes == parser.n_structural_indexes, + // then this will query parser.structural_indexes[parser.n_structural_indexes] which is len, + // otherwise, it will copy some prior index. + parser.structural_indexes[parser.n_structural_indexes + 1] = parser.structural_indexes[parser.n_structural_indexes]; + // This next line is critical, do not change it unless you understand what you are + // doing. + parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); + if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { + // We tolerate an unclosed string at the very end of the stream. Indeed, users + // often load their data in bulk without being careful and they want us to ignore + // the trailing garbage. + return EMPTY; + } + } + checker.check_eof(); + return checker.errors(); +} - } // namespace stage1 - } // unnamed namespace - } // namespace lsx +} // namespace stage1 +} // unnamed namespace +} // namespace lsx } // namespace simdjson // Clear CUSTOM_BIT_INDEXER so other implementations can set it if they need to. @@ -44866,38 +52941,38 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace { - namespace stage1 { +namespace lsx { +namespace { +namespace stage1 { - /** - * Validates that the string is actual UTF-8. - */ - template - bool generic_validate_utf8(const uint8_t* input, size_t length) { - checker c{}; - buf_block_reader<64> reader(input, length); - while (reader.has_full_block()) { - simd::simd8x64 in(reader.full_block()); - c.check_next_input(in); - reader.advance(); - } - uint8_t block[64]{}; - reader.get_remainder(block); - simd::simd8x64 in(block); - c.check_next_input(in); - reader.advance(); - c.check_eof(); - return c.errors() == error_code::SUCCESS; - } +/** + * Validates that the string is actual UTF-8. + */ +template +bool generic_validate_utf8(const uint8_t * input, size_t length) { + checker c{}; + buf_block_reader<64> reader(input, length); + while (reader.has_full_block()) { + simd::simd8x64 in(reader.full_block()); + c.check_next_input(in); + reader.advance(); + } + uint8_t block[64]{}; + reader.get_remainder(block); + simd::simd8x64 in(block); + c.check_next_input(in); + reader.advance(); + c.check_eof(); + return c.errors() == error_code::SUCCESS; +} - bool generic_validate_utf8(const char* input, size_t length) { - return generic_validate_utf8(reinterpret_cast(input), length); - } +bool generic_validate_utf8(const char * input, size_t length) { + return generic_validate_utf8(reinterpret_cast(input),length); +} - } // namespace stage1 - } // unnamed namespace - } // namespace lsx +} // namespace stage1 +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_UTF8_VALIDATOR_H @@ -44916,18 +52991,18 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace { - namespace stage2 { +namespace lsx { +namespace { +namespace stage2 { - class json_iterator; - class structural_iterator; - struct tape_builder; - struct tape_writer; +class json_iterator; +class structural_iterator; +struct tape_builder; +struct tape_writer; - } // namespace stage2 - } // unnamed namespace - } // namespace lsx +} // namespace stage2 +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_BASE_H @@ -44945,109 +53020,109 @@ namespace simdjson { #include namespace simdjson { - namespace lsx { - namespace { - namespace stage2 { +namespace lsx { +namespace { +namespace stage2 { - struct tape_writer { - /** The next place to write to tape */ - uint64_t* next_tape_loc; +struct tape_writer { + /** The next place to write to tape */ + uint64_t *next_tape_loc; - /** Write a signed 64-bit value to tape. */ - simdjson_inline void append_s64(int64_t value) noexcept; + /** Write a signed 64-bit value to tape. */ + simdjson_inline void append_s64(int64_t value) noexcept; - /** Write an unsigned 64-bit value to tape. */ - simdjson_inline void append_u64(uint64_t value) noexcept; + /** Write an unsigned 64-bit value to tape. */ + simdjson_inline void append_u64(uint64_t value) noexcept; - /** Write a double value to tape. */ - simdjson_inline void append_double(double value) noexcept; + /** Write a double value to tape. */ + simdjson_inline void append_double(double value) noexcept; - /** - * Append a tape entry (an 8-bit type,and 56 bits worth of value). - */ - simdjson_inline void append(uint64_t val, internal::tape_type t) noexcept; + /** + * Append a tape entry (an 8-bit type,and 56 bits worth of value). + */ + simdjson_inline void append(uint64_t val, internal::tape_type t) noexcept; - /** - * Skip the current tape entry without writing. - * - * Used to skip the start of the container, since we'll come back later to fill it in when the - * container ends. - */ - simdjson_inline void skip() noexcept; + /** + * Skip the current tape entry without writing. + * + * Used to skip the start of the container, since we'll come back later to fill it in when the + * container ends. + */ + simdjson_inline void skip() noexcept; - /** - * Skip the number of tape entries necessary to write a large u64 or i64. - */ - simdjson_inline void skip_large_integer() noexcept; + /** + * Skip the number of tape entries necessary to write a large u64 or i64. + */ + simdjson_inline void skip_large_integer() noexcept; - /** - * Skip the number of tape entries necessary to write a double. - */ - simdjson_inline void skip_double() noexcept; + /** + * Skip the number of tape entries necessary to write a double. + */ + simdjson_inline void skip_double() noexcept; - /** - * Write a value to a known location on tape. - * - * Used to go back and write out the start of a container after the container ends. - */ - simdjson_inline static void write(uint64_t& tape_loc, uint64_t val, internal::tape_type t) noexcept; + /** + * Write a value to a known location on tape. + * + * Used to go back and write out the start of a container after the container ends. + */ + simdjson_inline static void write(uint64_t &tape_loc, uint64_t val, internal::tape_type t) noexcept; - private: - /** - * Append both the tape entry, and a supplementary value following it. Used for types that need - * all 64 bits, such as double and uint64_t. - */ - template - simdjson_inline void append2(uint64_t val, T val2, internal::tape_type t) noexcept; - }; // struct tape_writer +private: + /** + * Append both the tape entry, and a supplementary value following it. Used for types that need + * all 64 bits, such as double and uint64_t. + */ + template + simdjson_inline void append2(uint64_t val, T val2, internal::tape_type t) noexcept; +}; // struct tape_writer - simdjson_inline void tape_writer::append_s64(int64_t value) noexcept { - append2(0, value, internal::tape_type::INT64); - } +simdjson_inline void tape_writer::append_s64(int64_t value) noexcept { + append2(0, value, internal::tape_type::INT64); +} - simdjson_inline void tape_writer::append_u64(uint64_t value) noexcept { - append(0, internal::tape_type::UINT64); - *next_tape_loc = value; - next_tape_loc++; - } +simdjson_inline void tape_writer::append_u64(uint64_t value) noexcept { + append(0, internal::tape_type::UINT64); + *next_tape_loc = value; + next_tape_loc++; +} - /** Write a double value to tape. */ - simdjson_inline void tape_writer::append_double(double value) noexcept { - append2(0, value, internal::tape_type::DOUBLE); - } +/** Write a double value to tape. */ +simdjson_inline void tape_writer::append_double(double value) noexcept { + append2(0, value, internal::tape_type::DOUBLE); +} - simdjson_inline void tape_writer::skip() noexcept { - next_tape_loc++; - } +simdjson_inline void tape_writer::skip() noexcept { + next_tape_loc++; +} - simdjson_inline void tape_writer::skip_large_integer() noexcept { - next_tape_loc += 2; - } +simdjson_inline void tape_writer::skip_large_integer() noexcept { + next_tape_loc += 2; +} - simdjson_inline void tape_writer::skip_double() noexcept { - next_tape_loc += 2; - } +simdjson_inline void tape_writer::skip_double() noexcept { + next_tape_loc += 2; +} - simdjson_inline void tape_writer::append(uint64_t val, internal::tape_type t) noexcept { - *next_tape_loc = val | ((uint64_t(char(t))) << 56); - next_tape_loc++; - } +simdjson_inline void tape_writer::append(uint64_t val, internal::tape_type t) noexcept { + *next_tape_loc = val | ((uint64_t(char(t))) << 56); + next_tape_loc++; +} - template - simdjson_inline void tape_writer::append2(uint64_t val, T val2, internal::tape_type t) noexcept { - append(val, t); - static_assert(sizeof(val2) == sizeof(*next_tape_loc), "Type is not 64 bits!"); - memcpy(next_tape_loc, &val2, sizeof(val2)); - next_tape_loc++; - } +template +simdjson_inline void tape_writer::append2(uint64_t val, T val2, internal::tape_type t) noexcept { + append(val, t); + static_assert(sizeof(val2) == sizeof(*next_tape_loc), "Type is not 64 bits!"); + memcpy(next_tape_loc, &val2, sizeof(val2)); + next_tape_loc++; +} - simdjson_inline void tape_writer::write(uint64_t& tape_loc, uint64_t val, internal::tape_type t) noexcept { - tape_loc = val | ((uint64_t(char(t))) << 56); - } +simdjson_inline void tape_writer::write(uint64_t &tape_loc, uint64_t val, internal::tape_type t) noexcept { + tape_loc = val | ((uint64_t(char(t))) << 56); +} - } // namespace stage2 - } // unnamed namespace - } // namespace lsx +} // namespace stage2 +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_WRITER_H @@ -45067,92 +53142,90 @@ namespace simdjson { // This is for an internal-only stage 2 specific logger. // Set LOG_ENABLED = true to log what stage 2 is doing! namespace simdjson { - namespace lsx { - namespace { - namespace logger { +namespace lsx { +namespace { +namespace logger { - static constexpr const char* DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"; + static constexpr const char * DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"; #if SIMDJSON_VERBOSE_LOGGING - static constexpr const bool LOG_ENABLED = true; + static constexpr const bool LOG_ENABLED = true; #else - static constexpr const bool LOG_ENABLED = false; + static constexpr const bool LOG_ENABLED = false; #endif - 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 constexpr const int LOG_INDEX_LEN = 5; + 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 constexpr const int LOG_INDEX_LEN = 5; - static int log_depth; // Not threadsafe. Log only. + static int log_depth; // Not threadsafe. Log only. - // Helper to turn unprintable or newline characters into spaces - static simdjson_inline char printable_char(char c) { - if (c >= 0x20) { - return c; - } - else { - return ' '; - } - } + // Helper to turn unprintable or newline characters into spaces + static simdjson_inline char printable_char(char c) { + if (c >= 0x20) { + return c; + } else { + return ' '; + } + } - // Print the header and set up log_start - static simdjson_inline void log_start() { - if (LOG_ENABLED) { - log_depth = 0; - printf("\n"); - printf("| %-*s | %-*s | %-*s | %-*s | Detail |\n", LOG_EVENT_LEN, "Event", LOG_BUFFER_LEN, "Buffer", LOG_SMALL_BUFFER_LEN, "Next", 5, "Next#"); - printf("|%.*s|%.*s|%.*s|%.*s|--------|\n", LOG_EVENT_LEN + 2, DASHES, LOG_BUFFER_LEN + 2, DASHES, LOG_SMALL_BUFFER_LEN + 2, DASHES, 5 + 2, DASHES); - } - } + // Print the header and set up log_start + static simdjson_inline void log_start() { + if (LOG_ENABLED) { + log_depth = 0; + printf("\n"); + printf("| %-*s | %-*s | %-*s | %-*s | Detail |\n", LOG_EVENT_LEN, "Event", LOG_BUFFER_LEN, "Buffer", LOG_SMALL_BUFFER_LEN, "Next", 5, "Next#"); + printf("|%.*s|%.*s|%.*s|%.*s|--------|\n", LOG_EVENT_LEN+2, DASHES, LOG_BUFFER_LEN+2, DASHES, LOG_SMALL_BUFFER_LEN+2, DASHES, 5+2, DASHES); + } + } - simdjson_unused static simdjson_inline void log_string(const char* message) { - if (LOG_ENABLED) { - printf("%s\n", message); - } - } + simdjson_unused static simdjson_inline void log_string(const char *message) { + if (LOG_ENABLED) { + printf("%s\n", message); + } + } - // Logs a single line from the stage 2 DOM parser - template - static simdjson_inline void log_line(S& structurals, const char* title_prefix, const char* title, const char* detail) { - if (LOG_ENABLED) { - printf("| %*s%s%-*s ", log_depth * 2, "", title_prefix, LOG_EVENT_LEN - log_depth * 2 - int(strlen(title_prefix)), title); - auto current_index = structurals.at_beginning() ? nullptr : structurals.next_structural - 1; - auto next_index = structurals.next_structural; - auto current = current_index ? &structurals.buf[*current_index] : reinterpret_cast(" "); - auto next = &structurals.buf[*next_index]; - { - // Print the next N characters in the buffer. - printf("| "); - // Otherwise, print the characters starting from the buffer position. - // Print spaces for unprintable or newline characters. - for (int i = 0; i < LOG_BUFFER_LEN; i++) { - printf("%c", printable_char(current[i])); - } - printf(" "); - // Print the next N characters in the buffer. - printf("| "); - // Otherwise, print the characters starting from the buffer position. - // Print spaces for unprintable or newline characters. - for (int i = 0; i < LOG_SMALL_BUFFER_LEN; i++) { - printf("%c", printable_char(next[i])); - } - printf(" "); - } - if (current_index) { - printf("| %*u ", LOG_INDEX_LEN, *current_index); - } - else { - printf("| %-*s ", LOG_INDEX_LEN, ""); - } - // printf("| %*u ", LOG_INDEX_LEN, structurals.next_tape_index()); - printf("| %-s ", detail); - printf("|\n"); - } - } + // Logs a single line from the stage 2 DOM parser + template + static simdjson_inline void log_line(S &structurals, const char *title_prefix, const char *title, const char *detail) { + if (LOG_ENABLED) { + printf("| %*s%s%-*s ", log_depth*2, "", title_prefix, LOG_EVENT_LEN - log_depth*2 - int(strlen(title_prefix)), title); + auto current_index = structurals.at_beginning() ? nullptr : structurals.next_structural-1; + auto next_index = structurals.next_structural; + auto current = current_index ? &structurals.buf[*current_index] : reinterpret_cast(" "); + auto next = &structurals.buf[*next_index]; + { + // Print the next N characters in the buffer. + printf("| "); + // Otherwise, print the characters starting from the buffer position. + // Print spaces for unprintable or newline characters. + for (int i=0;i - simdjson_warn_unused simdjson_inline error_code walk_document(V& visitor) noexcept; + /** + * Walk the JSON document. + * + * The visitor receives callbacks when values are encountered. All callbacks pass the iterator as + * the first parameter; some callbacks have other parameters as well: + * + * - visit_document_start() - at the beginning. + * - visit_document_end() - at the end (if things were successful). + * + * - visit_array_start() - at the start `[` of a non-empty array. + * - visit_array_end() - at the end `]` of a non-empty array. + * - visit_empty_array() - when an empty array is encountered. + * + * - visit_object_end() - at the start `]` of a non-empty object. + * - visit_object_start() - at the end `]` of a non-empty object. + * - visit_empty_object() - when an empty object is encountered. + * - visit_key(const uint8_t *key) - when a key in an object field is encountered. key is + * guaranteed to point at the first quote of the string (`"key"`). + * - visit_primitive(const uint8_t *value) - when a value is a string, number, boolean or null. + * - visit_root_primitive(iter, uint8_t *value) - when the top-level value is a string, number, boolean or null. + * + * - increment_count(iter) - each time a value is found in an array or object. + */ + template + simdjson_warn_unused simdjson_inline error_code walk_document(V &visitor) noexcept; - /** - * Create an iterator capable of walking a JSON document. - * - * The document must have already passed through stage 1. - */ - simdjson_inline json_iterator(dom_parser_implementation& _dom_parser, size_t start_structural_index); + /** + * Create an iterator capable of walking a JSON document. + * + * The document must have already passed through stage 1. + */ + simdjson_inline json_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index); - /** - * Look at the next token. - * - * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). - * - * They may include invalid JSON as well (such as `1.2.3` or `ture`). - */ - simdjson_inline const uint8_t* peek() const noexcept; - /** - * Advance to the next token. - * - * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). - * - * They may include invalid JSON as well (such as `1.2.3` or `ture`). - */ - simdjson_inline const uint8_t* advance() noexcept; - /** - * Get the remaining length of the document, from the start of the current token. - */ - simdjson_inline size_t remaining_len() const noexcept; - /** - * Check if we are at the end of the document. - * - * If this is true, there are no more tokens. - */ - simdjson_inline bool at_eof() const noexcept; - /** - * Check if we are at the beginning of the document. - */ - simdjson_inline bool at_beginning() const noexcept; - simdjson_inline uint8_t last_structural() const noexcept; + /** + * Look at the next token. + * + * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). + * + * They may include invalid JSON as well (such as `1.2.3` or `ture`). + */ + simdjson_inline const uint8_t *peek() const noexcept; + /** + * Advance to the next token. + * + * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). + * + * They may include invalid JSON as well (such as `1.2.3` or `ture`). + */ + simdjson_inline const uint8_t *advance() noexcept; + /** + * Get the remaining length of the document, from the start of the current token. + */ + simdjson_inline size_t remaining_len() const noexcept; + /** + * Check if we are at the end of the document. + * + * If this is true, there are no more tokens. + */ + simdjson_inline bool at_eof() const noexcept; + /** + * Check if we are at the beginning of the document. + */ + simdjson_inline bool at_beginning() const noexcept; + simdjson_inline uint8_t last_structural() const noexcept; - /** - * Log that a value has been found. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_value(const char* type) const noexcept; - /** - * Log the start of a multipart value. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_start_value(const char* type) const noexcept; - /** - * Log the end of a multipart value. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_end_value(const char* type) const noexcept; - /** - * Log an error. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_error(const char* error) const noexcept; + /** + * Log that a value has been found. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_value(const char *type) const noexcept; + /** + * Log the start of a multipart value. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_start_value(const char *type) const noexcept; + /** + * Log the end of a multipart value. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_end_value(const char *type) const noexcept; + /** + * Log an error. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_error(const char *error) const noexcept; - template - simdjson_warn_unused simdjson_inline error_code visit_root_primitive(V& visitor, const uint8_t* value) noexcept; - template - simdjson_warn_unused simdjson_inline error_code visit_primitive(V& visitor, const uint8_t* value) noexcept; - }; + template + simdjson_warn_unused simdjson_inline error_code visit_root_primitive(V &visitor, const uint8_t *value) noexcept; + template + simdjson_warn_unused simdjson_inline error_code visit_primitive(V &visitor, const uint8_t *value) noexcept; +}; - template - simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V& visitor) noexcept { - logger::log_start(); +template +simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &visitor) noexcept { + logger::log_start(); - // - // Start the document - // - if (at_eof()) { return EMPTY; } - log_start_value("document"); - SIMDJSON_TRY(visitor.visit_document_start(*this)); + // + // Start the document + // + if (at_eof()) { return EMPTY; } + log_start_value("document"); + SIMDJSON_TRY( visitor.visit_document_start(*this) ); - // - // Read first value - // - { - auto value = advance(); + // + // Read first value + // + { + auto value = advance(); - // Make sure the outer object or array is closed before continuing; otherwise, there are ways we - // could get into memory corruption. See https://github.com/simdjson/simdjson/issues/906 - if (!STREAMING) { - switch (*value) { - case '{': if (last_structural() != '}') { log_value("starting brace unmatched"); return TAPE_ERROR; }; break; - case '[': if (last_structural() != ']') { log_value("starting bracket unmatched"); return TAPE_ERROR; }; break; - } - } + // Make sure the outer object or array is closed before continuing; otherwise, there are ways we + // could get into memory corruption. See https://github.com/simdjson/simdjson/issues/906 + if (!STREAMING) { + switch (*value) { + case '{': if (last_structural() != '}') { log_value("starting brace unmatched"); return TAPE_ERROR; }; break; + case '[': if (last_structural() != ']') { log_value("starting bracket unmatched"); return TAPE_ERROR; }; break; + } + } - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY(visitor.visit_empty_object(*this)); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY(visitor.visit_empty_array(*this)); break; } goto array_begin; - default: SIMDJSON_TRY(visitor.visit_root_primitive(*this, value)); break; - } - } - goto document_end; + switch (*value) { + case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; + case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; + default: SIMDJSON_TRY( visitor.visit_root_primitive(*this, value) ); break; + } + } + goto document_end; - // - // Object parser states - // - object_begin: - log_start_value("object"); - depth++; - if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } - dom_parser.is_array[depth] = false; - SIMDJSON_TRY(visitor.visit_object_start(*this)); +// +// Object parser states +// +object_begin: + log_start_value("object"); + depth++; + if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } + dom_parser.is_array[depth] = false; + SIMDJSON_TRY( visitor.visit_object_start(*this) ); - { - auto key = advance(); - if (*key != '"') { log_error("Object does not start with a key"); return TAPE_ERROR; } - SIMDJSON_TRY(visitor.increment_count(*this)); - SIMDJSON_TRY(visitor.visit_key(*this, key)); - } + { + auto key = advance(); + if (*key != '"') { log_error("Object does not start with a key"); return TAPE_ERROR; } + SIMDJSON_TRY( visitor.increment_count(*this) ); + SIMDJSON_TRY( visitor.visit_key(*this, key) ); + } - object_field: - if (simdjson_unlikely(*advance() != ':')) { log_error("Missing colon after key in object"); return TAPE_ERROR; } - { - auto value = advance(); - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY(visitor.visit_empty_object(*this)); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY(visitor.visit_empty_array(*this)); break; } goto array_begin; - default: SIMDJSON_TRY(visitor.visit_primitive(*this, value)); break; - } - } +object_field: + if (simdjson_unlikely( *advance() != ':' )) { log_error("Missing colon after key in object"); return TAPE_ERROR; } + { + auto value = advance(); + switch (*value) { + case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; + case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; + default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break; + } + } - object_continue: - switch (*advance()) { - case ',': - SIMDJSON_TRY(visitor.increment_count(*this)); - { - auto key = advance(); - if (simdjson_unlikely(*key != '"')) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; } - SIMDJSON_TRY(visitor.visit_key(*this, key)); - } - goto object_field; - case '}': log_end_value("object"); SIMDJSON_TRY(visitor.visit_object_end(*this)); goto scope_end; - default: log_error("No comma between object fields"); return TAPE_ERROR; - } +object_continue: + switch (*advance()) { + case ',': + SIMDJSON_TRY( visitor.increment_count(*this) ); + { + auto key = advance(); + if (simdjson_unlikely( *key != '"' )) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; } + SIMDJSON_TRY( visitor.visit_key(*this, key) ); + } + goto object_field; + case '}': log_end_value("object"); SIMDJSON_TRY( visitor.visit_object_end(*this) ); goto scope_end; + default: log_error("No comma between object fields"); return TAPE_ERROR; + } - scope_end: - depth--; - if (depth == 0) { goto document_end; } - if (dom_parser.is_array[depth]) { goto array_continue; } - goto object_continue; +scope_end: + depth--; + if (depth == 0) { goto document_end; } + if (dom_parser.is_array[depth]) { goto array_continue; } + goto object_continue; - // - // Array parser states - // - array_begin: - log_start_value("array"); - depth++; - if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } - dom_parser.is_array[depth] = true; - SIMDJSON_TRY(visitor.visit_array_start(*this)); - SIMDJSON_TRY(visitor.increment_count(*this)); +// +// Array parser states +// +array_begin: + log_start_value("array"); + depth++; + if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } + dom_parser.is_array[depth] = true; + SIMDJSON_TRY( visitor.visit_array_start(*this) ); + SIMDJSON_TRY( visitor.increment_count(*this) ); - array_value: - { - auto value = advance(); - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY(visitor.visit_empty_object(*this)); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY(visitor.visit_empty_array(*this)); break; } goto array_begin; - default: SIMDJSON_TRY(visitor.visit_primitive(*this, value)); break; - } - } +array_value: + { + auto value = advance(); + switch (*value) { + case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; + case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; + default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break; + } + } - array_continue: - switch (*advance()) { - case ',': SIMDJSON_TRY(visitor.increment_count(*this)); goto array_value; - case ']': log_end_value("array"); SIMDJSON_TRY(visitor.visit_array_end(*this)); goto scope_end; - default: log_error("Missing comma between array values"); return TAPE_ERROR; - } +array_continue: + switch (*advance()) { + case ',': SIMDJSON_TRY( visitor.increment_count(*this) ); goto array_value; + case ']': log_end_value("array"); SIMDJSON_TRY( visitor.visit_array_end(*this) ); goto scope_end; + default: log_error("Missing comma between array values"); return TAPE_ERROR; + } - document_end: - log_end_value("document"); - SIMDJSON_TRY(visitor.visit_document_end(*this)); +document_end: + log_end_value("document"); + SIMDJSON_TRY( visitor.visit_document_end(*this) ); - dom_parser.next_structural_index = uint32_t(next_structural - &dom_parser.structural_indexes[0]); + dom_parser.next_structural_index = uint32_t(next_structural - &dom_parser.structural_indexes[0]); - // If we didn't make it to the end, it's an error - if (!STREAMING && dom_parser.next_structural_index != dom_parser.n_structural_indexes) { - log_error("More than one JSON value at the root of the document, or extra characters at the end of the JSON!"); - return TAPE_ERROR; - } + // If we didn't make it to the end, it's an error + if ( !STREAMING && dom_parser.next_structural_index != dom_parser.n_structural_indexes ) { + log_error("More than one JSON value at the root of the document, or extra characters at the end of the JSON!"); + return TAPE_ERROR; + } - return SUCCESS; + return SUCCESS; - } // walk_document() +} // walk_document() - simdjson_inline json_iterator::json_iterator(dom_parser_implementation& _dom_parser, size_t start_structural_index) - : buf{ _dom_parser.buf }, - next_structural{ &_dom_parser.structural_indexes[start_structural_index] }, - dom_parser{ _dom_parser } { - } +simdjson_inline json_iterator::json_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index) + : buf{_dom_parser.buf}, + next_structural{&_dom_parser.structural_indexes[start_structural_index]}, + dom_parser{_dom_parser} { +} - simdjson_inline const uint8_t* json_iterator::peek() const noexcept { - return &buf[*(next_structural)]; - } - simdjson_inline const uint8_t* json_iterator::advance() noexcept { - return &buf[*(next_structural++)]; - } - simdjson_inline size_t json_iterator::remaining_len() const noexcept { - return dom_parser.len - *(next_structural - 1); - } +simdjson_inline const uint8_t *json_iterator::peek() const noexcept { + return &buf[*(next_structural)]; +} +simdjson_inline const uint8_t *json_iterator::advance() noexcept { + return &buf[*(next_structural++)]; +} +simdjson_inline size_t json_iterator::remaining_len() const noexcept { + return dom_parser.len - *(next_structural-1); +} - simdjson_inline bool json_iterator::at_eof() const noexcept { - return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; - } - simdjson_inline bool json_iterator::at_beginning() const noexcept { - return next_structural == dom_parser.structural_indexes.get(); - } - simdjson_inline uint8_t json_iterator::last_structural() const noexcept { - return buf[dom_parser.structural_indexes[dom_parser.n_structural_indexes - 1]]; - } +simdjson_inline bool json_iterator::at_eof() const noexcept { + return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; +} +simdjson_inline bool json_iterator::at_beginning() const noexcept { + return next_structural == dom_parser.structural_indexes.get(); +} +simdjson_inline uint8_t json_iterator::last_structural() const noexcept { + return buf[dom_parser.structural_indexes[dom_parser.n_structural_indexes - 1]]; +} - simdjson_inline void json_iterator::log_value(const char* type) const noexcept { - logger::log_line(*this, "", type, ""); - } +simdjson_inline void json_iterator::log_value(const char *type) const noexcept { + logger::log_line(*this, "", type, ""); +} - simdjson_inline void json_iterator::log_start_value(const char* type) const noexcept { - logger::log_line(*this, "+", type, ""); - if (logger::LOG_ENABLED) { logger::log_depth++; } - } +simdjson_inline void json_iterator::log_start_value(const char *type) const noexcept { + logger::log_line(*this, "+", type, ""); + if (logger::LOG_ENABLED) { logger::log_depth++; } +} - simdjson_inline void json_iterator::log_end_value(const char* type) const noexcept { - if (logger::LOG_ENABLED) { logger::log_depth--; } - logger::log_line(*this, "-", type, ""); - } +simdjson_inline void json_iterator::log_end_value(const char *type) const noexcept { + if (logger::LOG_ENABLED) { logger::log_depth--; } + logger::log_line(*this, "-", type, ""); +} - simdjson_inline void json_iterator::log_error(const char* error) const noexcept { - logger::log_line(*this, "", "ERROR", error); - } +simdjson_inline void json_iterator::log_error(const char *error) const noexcept { + logger::log_line(*this, "", "ERROR", error); +} - template - simdjson_warn_unused simdjson_inline error_code json_iterator::visit_root_primitive(V& visitor, const uint8_t* value) noexcept { - switch (*value) { - case '"': return visitor.visit_root_string(*this, value); - case 't': return visitor.visit_root_true_atom(*this, value); - case 'f': return visitor.visit_root_false_atom(*this, value); - case 'n': return visitor.visit_root_null_atom(*this, value); - case '-': - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - return visitor.visit_root_number(*this, value); - default: - log_error("Document starts with a non-value character"); - return TAPE_ERROR; - } - } - template - simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V& visitor, const uint8_t* value) noexcept { - // Use the fact that most scalars are going to be either strings or numbers. - if (*value == '"') { - return visitor.visit_string(*this, value); - } - else if (((*value - '0') < 10) || (*value == '-')) { - return visitor.visit_number(*this, value); - } - // true, false, null are uncommon. - switch (*value) { - case 't': return visitor.visit_true_atom(*this, value); - case 'f': return visitor.visit_false_atom(*this, value); - case 'n': return visitor.visit_null_atom(*this, value); - default: - log_error("Non-value found when value was expected!"); - return TAPE_ERROR; - } - } +template +simdjson_warn_unused simdjson_inline error_code json_iterator::visit_root_primitive(V &visitor, const uint8_t *value) noexcept { + switch (*value) { + case '"': return visitor.visit_root_string(*this, value); + case 't': return visitor.visit_root_true_atom(*this, value); + case 'f': return visitor.visit_root_false_atom(*this, value); + case 'n': return visitor.visit_root_null_atom(*this, value); + case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return visitor.visit_root_number(*this, value); + default: + log_error("Document starts with a non-value character"); + return TAPE_ERROR; + } +} +template +simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V &visitor, const uint8_t *value) noexcept { + // Use the fact that most scalars are going to be either strings or numbers. + if(*value == '"') { + return visitor.visit_string(*this, value); + } else if (((*value - '0') < 10) || (*value == '-')) { + return visitor.visit_number(*this, value); + } + // true, false, null are uncommon. + switch (*value) { + case 't': return visitor.visit_true_atom(*this, value); + case 'f': return visitor.visit_false_atom(*this, value); + case 'n': return visitor.visit_null_atom(*this, value); + default: + log_error("Non-value found when value was expected!"); + return TAPE_ERROR; + } +} - } // namespace stage2 - } // unnamed namespace - } // namespace lsx +} // namespace stage2 +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_JSON_ITERATOR_H /* end file generic/stage2/json_iterator.h for lsx */ /* including generic/stage2/stringparsing.h for lsx: #include */ /* begin file generic/stage2/stringparsing.h for lsx */ +#include #ifndef SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -45505,242 +53578,238 @@ namespace simdjson { // It is intended to be included multiple times and compiled multiple times namespace simdjson { - namespace lsx { - namespace { - /// @private - namespace stringparsing { +namespace lsx { +namespace { +/// @private +namespace stringparsing { - // begin copypasta - // These chars yield themselves: " \ / - // b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab - // u not handled in this table as it's complex - static const uint8_t escape_map[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// begin copypasta +// These chars yield themselves: " \ / +// b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab +// u not handled in this table as it's complex +static const uint8_t escape_map[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0. + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5. - 0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6. - 0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7. + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4. + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5. + 0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6. + 0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - }; + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; - // handle a unicode codepoint - // write appropriate values into dest - // src will advance 6 bytes or 12 bytes - // dest will advance a variable amount (return via pointer) - // return true if the unicode codepoint was valid - // We work in little-endian then swap at write time - simdjson_warn_unused - simdjson_inline bool handle_unicode_codepoint(const uint8_t** src_ptr, - uint8_t** dst_ptr, bool allow_replacement) { - // Use the default Unicode Character 'REPLACEMENT CHARACTER' (U+FFFD) - constexpr uint32_t substitution_code_point = 0xfffd; - // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the - // conversion is not valid; we defer the check for this to inside the - // multilingual plane check. - uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); - *src_ptr += 6; +// handle a unicode codepoint +// write appropriate values into dest +// src will advance 6 bytes or 12 bytes +// dest will advance a variable amount (return via pointer) +// return true if the unicode codepoint was valid +// We work in little-endian then swap at write time +simdjson_warn_unused +simdjson_inline bool handle_unicode_codepoint(const uint8_t **src_ptr, + uint8_t **dst_ptr, bool allow_replacement) { + // Use the default Unicode Character 'REPLACEMENT CHARACTER' (U+FFFD) + constexpr uint32_t substitution_code_point = 0xfffd; + // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the + // conversion is not valid; we defer the check for this to inside the + // multilingual plane check. + uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); + *src_ptr += 6; - // If we found a high surrogate, we must - // check for low surrogate for characters - // outside the Basic - // Multilingual Plane. - if (code_point >= 0xd800 && code_point < 0xdc00) { - const uint8_t* src_data = *src_ptr; - /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ - if (((src_data[0] << 8) | src_data[1]) != ((static_cast ('\\') << 8) | static_cast ('u'))) { - if (!allow_replacement) { return false; } - code_point = substitution_code_point; - } - else { - uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); + // If we found a high surrogate, we must + // check for low surrogate for characters + // outside the Basic + // Multilingual Plane. + if (code_point >= 0xd800 && code_point < 0xdc00) { + const uint8_t *src_data = *src_ptr; + /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ + if (((src_data[0] << 8) | src_data[1]) != ((static_cast ('\\') << 8) | static_cast ('u'))) { + if(!allow_replacement) { return false; } + code_point = substitution_code_point; + } else { + uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); - // We have already checked that the high surrogate is valid and - // (code_point - 0xd800) < 1024. - // - // Check that code_point_2 is in the range 0xdc00..0xdfff - // and that code_point_2 was parsed from valid hex. - uint32_t low_bit = code_point_2 - 0xdc00; - if (low_bit >> 10) { - if (!allow_replacement) { return false; } - code_point = substitution_code_point; - } - else { - code_point = (((code_point - 0xd800) << 10) | low_bit) + 0x10000; - *src_ptr += 6; - } + // We have already checked that the high surrogate is valid and + // (code_point - 0xd800) < 1024. + // + // Check that code_point_2 is in the range 0xdc00..0xdfff + // and that code_point_2 was parsed from valid hex. + uint32_t low_bit = code_point_2 - 0xdc00; + if (low_bit >> 10) { + if(!allow_replacement) { return false; } + code_point = substitution_code_point; + } else { + code_point = (((code_point - 0xd800) << 10) | low_bit) + 0x10000; + *src_ptr += 6; + } - } - } - else if (code_point >= 0xdc00 && code_point <= 0xdfff) { - // If we encounter a low surrogate (not preceded by a high surrogate) - // then we have an error. - if (!allow_replacement) { return false; } - code_point = substitution_code_point; - } - size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); - *dst_ptr += offset; - return offset > 0; - } + } + } else if (code_point >= 0xdc00 && code_point <= 0xdfff) { + // If we encounter a low surrogate (not preceded by a high surrogate) + // then we have an error. + if(!allow_replacement) { return false; } + code_point = substitution_code_point; + } + size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); + *dst_ptr += offset; + return offset > 0; +} - // handle a unicode codepoint using the wobbly convention - // https://simonsapin.github.io/wtf-8/ - // write appropriate values into dest - // src will advance 6 bytes or 12 bytes - // dest will advance a variable amount (return via pointer) - // return true if the unicode codepoint was valid - // We work in little-endian then swap at write time - simdjson_warn_unused - simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t** src_ptr, - uint8_t** dst_ptr) { - // It is not ideal that this function is nearly identical to handle_unicode_codepoint. - // - // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the - // conversion is not valid; we defer the check for this to inside the - // multilingual plane check. - uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); - *src_ptr += 6; - // If we found a high surrogate, we must - // check for low surrogate for characters - // outside the Basic - // Multilingual Plane. - if (code_point >= 0xd800 && code_point < 0xdc00) { - const uint8_t* src_data = *src_ptr; - /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ - if (((src_data[0] << 8) | src_data[1]) == ((static_cast ('\\') << 8) | static_cast ('u'))) { - uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); - uint32_t low_bit = code_point_2 - 0xdc00; - if ((low_bit >> 10) == 0) { - code_point = - (((code_point - 0xd800) << 10) | low_bit) + 0x10000; - *src_ptr += 6; - } - } - } +// handle a unicode codepoint using the wobbly convention +// https://simonsapin.github.io/wtf-8/ +// write appropriate values into dest +// src will advance 6 bytes or 12 bytes +// dest will advance a variable amount (return via pointer) +// return true if the unicode codepoint was valid +// We work in little-endian then swap at write time +simdjson_warn_unused +simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t **src_ptr, + uint8_t **dst_ptr) { + // It is not ideal that this function is nearly identical to handle_unicode_codepoint. + // + // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the + // conversion is not valid; we defer the check for this to inside the + // multilingual plane check. + uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); + *src_ptr += 6; + // If we found a high surrogate, we must + // check for low surrogate for characters + // outside the Basic + // Multilingual Plane. + if (code_point >= 0xd800 && code_point < 0xdc00) { + const uint8_t *src_data = *src_ptr; + /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ + if (((src_data[0] << 8) | src_data[1]) == ((static_cast ('\\') << 8) | static_cast ('u'))) { + uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); + uint32_t low_bit = code_point_2 - 0xdc00; + if ((low_bit >> 10) == 0) { + code_point = + (((code_point - 0xd800) << 10) | low_bit) + 0x10000; + *src_ptr += 6; + } + } + } - size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); - *dst_ptr += offset; - return offset > 0; - } + size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); + *dst_ptr += offset; + return offset > 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. - */ - simdjson_warn_unused simdjson_inline uint8_t* parse_string(const uint8_t* src, uint8_t* dst, bool allow_replacement) { - while (1) { - // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); - // If the next thing is the end quote, copy and return - if (bs_quote.has_quote_first()) { - // we encountered quotes first. Move dst to point to quotes and exit - return dst + bs_quote.quote_index(); - } - if (bs_quote.has_backslash()) { - /* find out where the backspace is */ - auto bs_dist = bs_quote.backslash_index(); - uint8_t escape_char = src[bs_dist + 1]; - /* we encountered backslash first. Handle backslash */ - if (escape_char == 'u') { - /* move src/dst up to the start; they will be further adjusted - within the unicode codepoint handling code. */ - src += bs_dist; - dst += bs_dist; - if (!handle_unicode_codepoint(&src, &dst, allow_replacement)) { - return nullptr; - } - } - else { - /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and - * write bs_dist+1 characters to output - * note this may reach beyond the part of the buffer we've actually - * seen. I think this is ok */ - uint8_t escape_result = escape_map[escape_char]; - if (escape_result == 0u) { - return nullptr; /* bogus escape value is an error */ - } - dst[bs_dist] = escape_result; - src += bs_dist + 2; - dst += bs_dist + 1; - } - } - else { - /* they are the same. Since they can't co-occur, it means we - * encountered neither. */ - src += backslash_and_quote::BYTES_PROCESSED; - dst += backslash_and_quote::BYTES_PROCESSED; - } - } - } +/** + * 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. + */ +simdjson_warn_unused simdjson_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) { + while (1) { + // Copy the next n bytes, and find the backslash and quote in them. + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); + // If the next thing is the end quote, copy and return + if (bs_quote.has_quote_first()) { + // we encountered quotes first. Move dst to point to quotes and exit + return dst + bs_quote.quote_index(); + } + if (bs_quote.has_backslash()) { + /* find out where the backspace is */ + auto bs_dist = bs_quote.backslash_index(); + uint8_t escape_char = src[bs_dist + 1]; + /* we encountered backslash first. Handle backslash */ + if (escape_char == 'u') { + /* move src/dst up to the start; they will be further adjusted + within the unicode codepoint handling code. */ + src += bs_dist; + dst += bs_dist; + if (!handle_unicode_codepoint(&src, &dst, allow_replacement)) { + return nullptr; + } + } else { + /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and + * write bs_dist+1 characters to output + * note this may reach beyond the part of the buffer we've actually + * seen. I think this is ok */ + uint8_t escape_result = escape_map[escape_char]; + if (escape_result == 0u) { + return nullptr; /* bogus escape value is an error */ + } + dst[bs_dist] = escape_result; + src += bs_dist + 2; + dst += bs_dist + 1; + } + } else { + /* they are the same. Since they can't co-occur, it means we + * encountered neither. */ + src += backslash_and_quote::BYTES_PROCESSED; + dst += backslash_and_quote::BYTES_PROCESSED; + } + } +} - simdjson_warn_unused simdjson_inline uint8_t* parse_wobbly_string(const uint8_t* src, uint8_t* dst) { - // It is not ideal that this function is nearly identical to parse_string. - while (1) { - // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); - // If the next thing is the end quote, copy and return - if (bs_quote.has_quote_first()) { - // we encountered quotes first. Move dst to point to quotes and exit - return dst + bs_quote.quote_index(); - } - if (bs_quote.has_backslash()) { - /* find out where the backspace is */ - auto bs_dist = bs_quote.backslash_index(); - uint8_t escape_char = src[bs_dist + 1]; - /* we encountered backslash first. Handle backslash */ - if (escape_char == 'u') { - /* move src/dst up to the start; they will be further adjusted - within the unicode codepoint handling code. */ - src += bs_dist; - dst += bs_dist; - if (!handle_unicode_codepoint_wobbly(&src, &dst)) { - return nullptr; - } - } - else { - /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and - * write bs_dist+1 characters to output - * note this may reach beyond the part of the buffer we've actually - * seen. I think this is ok */ - uint8_t escape_result = escape_map[escape_char]; - if (escape_result == 0u) { - return nullptr; /* bogus escape value is an error */ - } - dst[bs_dist] = escape_result; - src += bs_dist + 2; - dst += bs_dist + 1; - } - } - else { - /* they are the same. Since they can't co-occur, it means we - * encountered neither. */ - src += backslash_and_quote::BYTES_PROCESSED; - dst += backslash_and_quote::BYTES_PROCESSED; - } - } - } +simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) { + // It is not ideal that this function is nearly identical to parse_string. + while (1) { + // Copy the next n bytes, and find the backslash and quote in them. + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); + // If the next thing is the end quote, copy and return + if (bs_quote.has_quote_first()) { + // we encountered quotes first. Move dst to point to quotes and exit + return dst + bs_quote.quote_index(); + } + if (bs_quote.has_backslash()) { + /* find out where the backspace is */ + auto bs_dist = bs_quote.backslash_index(); + uint8_t escape_char = src[bs_dist + 1]; + /* we encountered backslash first. Handle backslash */ + if (escape_char == 'u') { + /* move src/dst up to the start; they will be further adjusted + within the unicode codepoint handling code. */ + src += bs_dist; + dst += bs_dist; + if (!handle_unicode_codepoint_wobbly(&src, &dst)) { + return nullptr; + } + } else { + /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and + * write bs_dist+1 characters to output + * note this may reach beyond the part of the buffer we've actually + * seen. I think this is ok */ + uint8_t escape_result = escape_map[escape_char]; + if (escape_result == 0u) { + return nullptr; /* bogus escape value is an error */ + } + dst[bs_dist] = escape_result; + src += bs_dist + 2; + dst += bs_dist + 1; + } + } else { + /* they are the same. Since they can't co-occur, it means we + * encountered neither. */ + src += backslash_and_quote::BYTES_PROCESSED; + dst += backslash_and_quote::BYTES_PROCESSED; + } + } +} - } // namespace stringparsing - } // unnamed namespace - } // namespace lsx +} // namespace stringparsing + +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H @@ -45756,58 +53825,58 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace { - namespace stage2 { +namespace lsx { +namespace { +namespace stage2 { - class structural_iterator { - public: - const uint8_t* const buf; - uint32_t* next_structural; - dom_parser_implementation& dom_parser; +class structural_iterator { +public: + const uint8_t* const buf; + uint32_t *next_structural; + dom_parser_implementation &dom_parser; - // Start a structural - simdjson_inline structural_iterator(dom_parser_implementation& _dom_parser, size_t start_structural_index) - : buf{ _dom_parser.buf }, - next_structural{ &_dom_parser.structural_indexes[start_structural_index] }, - dom_parser{ _dom_parser } { - } - // Get the buffer position of the current structural character - simdjson_inline const uint8_t* current() { - return &buf[*(next_structural - 1)]; - } - // Get the current structural character - simdjson_inline char current_char() { - return buf[*(next_structural - 1)]; - } - // Get the next structural character without advancing - simdjson_inline char peek_next_char() { - return buf[*next_structural]; - } - simdjson_inline const uint8_t* peek() { - return &buf[*next_structural]; - } - simdjson_inline const uint8_t* advance() { - return &buf[*(next_structural++)]; - } - simdjson_inline char advance_char() { - return buf[*(next_structural++)]; - } - simdjson_inline size_t remaining_len() { - return dom_parser.len - *(next_structural - 1); - } + // Start a structural + simdjson_inline structural_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index) + : buf{_dom_parser.buf}, + next_structural{&_dom_parser.structural_indexes[start_structural_index]}, + dom_parser{_dom_parser} { + } + // Get the buffer position of the current structural character + simdjson_inline const uint8_t* current() { + return &buf[*(next_structural-1)]; + } + // Get the current structural character + simdjson_inline char current_char() { + return buf[*(next_structural-1)]; + } + // Get the next structural character without advancing + simdjson_inline char peek_next_char() { + return buf[*next_structural]; + } + simdjson_inline const uint8_t* peek() { + return &buf[*next_structural]; + } + simdjson_inline const uint8_t* advance() { + return &buf[*(next_structural++)]; + } + simdjson_inline char advance_char() { + return buf[*(next_structural++)]; + } + simdjson_inline size_t remaining_len() { + return dom_parser.len - *(next_structural-1); + } - simdjson_inline bool at_end() { - return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; - } - simdjson_inline bool at_beginning() { - return next_structural == dom_parser.structural_indexes.get(); - } - }; + simdjson_inline bool at_end() { + return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; + } + simdjson_inline bool at_beginning() { + return next_structural == dom_parser.structural_indexes.get(); + } +}; - } // namespace stage2 - } // unnamed namespace - } // namespace lsx +} // namespace stage2 +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_STRUCTURAL_ITERATOR_H @@ -45830,284 +53899,284 @@ namespace simdjson { namespace simdjson { - namespace lsx { - namespace { - namespace stage2 { +namespace lsx { +namespace { +namespace stage2 { - struct tape_builder { - template - simdjson_warn_unused static simdjson_inline error_code parse_document( - dom_parser_implementation& dom_parser, - dom::document& doc) noexcept; +struct tape_builder { + template + simdjson_warn_unused static simdjson_inline error_code parse_document( + dom_parser_implementation &dom_parser, + dom::document &doc) noexcept; - /** Called when a non-empty document starts. */ - simdjson_warn_unused simdjson_inline error_code visit_document_start(json_iterator& iter) noexcept; - /** Called when a non-empty document ends without error. */ - simdjson_warn_unused simdjson_inline error_code visit_document_end(json_iterator& iter) noexcept; + /** Called when a non-empty document starts. */ + simdjson_warn_unused simdjson_inline error_code visit_document_start(json_iterator &iter) noexcept; + /** Called when a non-empty document ends without error. */ + simdjson_warn_unused simdjson_inline error_code visit_document_end(json_iterator &iter) noexcept; - /** Called when a non-empty array starts. */ - simdjson_warn_unused simdjson_inline error_code visit_array_start(json_iterator& iter) noexcept; - /** Called when a non-empty array ends. */ - simdjson_warn_unused simdjson_inline error_code visit_array_end(json_iterator& iter) noexcept; - /** Called when an empty array is found. */ - simdjson_warn_unused simdjson_inline error_code visit_empty_array(json_iterator& iter) noexcept; + /** Called when a non-empty array starts. */ + simdjson_warn_unused simdjson_inline error_code visit_array_start(json_iterator &iter) noexcept; + /** Called when a non-empty array ends. */ + simdjson_warn_unused simdjson_inline error_code visit_array_end(json_iterator &iter) noexcept; + /** Called when an empty array is found. */ + simdjson_warn_unused simdjson_inline error_code visit_empty_array(json_iterator &iter) noexcept; - /** Called when a non-empty object starts. */ - simdjson_warn_unused simdjson_inline error_code visit_object_start(json_iterator& iter) noexcept; - /** - * Called when a key in a field is encountered. - * - * primitive, visit_object_start, visit_empty_object, visit_array_start, or visit_empty_array - * will be called after this with the field value. - */ - simdjson_warn_unused simdjson_inline error_code visit_key(json_iterator& iter, const uint8_t* key) noexcept; - /** Called when a non-empty object ends. */ - simdjson_warn_unused simdjson_inline error_code visit_object_end(json_iterator& iter) noexcept; - /** Called when an empty object is found. */ - simdjson_warn_unused simdjson_inline error_code visit_empty_object(json_iterator& iter) noexcept; + /** Called when a non-empty object starts. */ + simdjson_warn_unused simdjson_inline error_code visit_object_start(json_iterator &iter) noexcept; + /** + * Called when a key in a field is encountered. + * + * primitive, visit_object_start, visit_empty_object, visit_array_start, or visit_empty_array + * will be called after this with the field value. + */ + simdjson_warn_unused simdjson_inline error_code visit_key(json_iterator &iter, const uint8_t *key) noexcept; + /** Called when a non-empty object ends. */ + simdjson_warn_unused simdjson_inline error_code visit_object_end(json_iterator &iter) noexcept; + /** Called when an empty object is found. */ + simdjson_warn_unused simdjson_inline error_code visit_empty_object(json_iterator &iter) noexcept; - /** - * Called when a string, number, boolean or null is found. - */ - simdjson_warn_unused simdjson_inline error_code visit_primitive(json_iterator& iter, const uint8_t* value) noexcept; - /** - * Called when a string, number, boolean or null is found at the top level of a document (i.e. - * when there is no array or object and the entire document is a single string, number, boolean or - * null. - * - * This is separate from primitive() because simdjson's normal primitive parsing routines assume - * there is at least one more token after the value, which is only true in an array or object. - */ - simdjson_warn_unused simdjson_inline error_code visit_root_primitive(json_iterator& iter, const uint8_t* value) noexcept; + /** + * Called when a string, number, boolean or null is found. + */ + simdjson_warn_unused simdjson_inline error_code visit_primitive(json_iterator &iter, const uint8_t *value) noexcept; + /** + * Called when a string, number, boolean or null is found at the top level of a document (i.e. + * when there is no array or object and the entire document is a single string, number, boolean or + * null. + * + * This is separate from primitive() because simdjson's normal primitive parsing routines assume + * there is at least one more token after the value, which is only true in an array or object. + */ + simdjson_warn_unused simdjson_inline error_code visit_root_primitive(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_string(json_iterator& iter, const uint8_t* value, bool key = false) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_number(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_true_atom(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_false_atom(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_null_atom(json_iterator& iter, const uint8_t* value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_string(json_iterator &iter, const uint8_t *value, bool key = false) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_number(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_true_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_false_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_null_atom(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_string(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_number(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_true_atom(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_false_atom(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_null_atom(json_iterator& iter, const uint8_t* value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_string(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_number(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_true_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept; - /** Called each time a new field or element in an array or object is found. */ - simdjson_warn_unused simdjson_inline error_code increment_count(json_iterator& iter) noexcept; + /** Called each time a new field or element in an array or object is found. */ + simdjson_warn_unused simdjson_inline error_code increment_count(json_iterator &iter) noexcept; - /** Next location to write to tape */ - tape_writer tape; - private: - /** Next write location in the string buf for stage 2 parsing */ - uint8_t* current_string_buf_loc; + /** Next location to write to tape */ + tape_writer tape; +private: + /** Next write location in the string buf for stage 2 parsing */ + uint8_t *current_string_buf_loc; - simdjson_inline tape_builder(dom::document& doc) noexcept; + simdjson_inline tape_builder(dom::document &doc) noexcept; - simdjson_inline uint32_t next_tape_index(json_iterator& iter) const noexcept; - simdjson_inline void start_container(json_iterator& iter) noexcept; - simdjson_warn_unused simdjson_inline error_code end_container(json_iterator& iter, internal::tape_type start, internal::tape_type end) noexcept; - simdjson_warn_unused simdjson_inline error_code empty_container(json_iterator& iter, internal::tape_type start, internal::tape_type end) noexcept; - simdjson_inline uint8_t* on_start_string(json_iterator& iter) noexcept; - simdjson_inline void on_end_string(uint8_t* dst) noexcept; - }; // struct tape_builder + simdjson_inline uint32_t next_tape_index(json_iterator &iter) const noexcept; + simdjson_inline void start_container(json_iterator &iter) noexcept; + simdjson_warn_unused simdjson_inline error_code end_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept; + simdjson_warn_unused simdjson_inline error_code empty_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept; + simdjson_inline uint8_t *on_start_string(json_iterator &iter) noexcept; + simdjson_inline void on_end_string(uint8_t *dst) noexcept; +}; // struct tape_builder - template - simdjson_warn_unused simdjson_inline error_code tape_builder::parse_document( - dom_parser_implementation& dom_parser, - dom::document& doc) noexcept { - dom_parser.doc = &doc; - json_iterator iter(dom_parser, STREAMING ? dom_parser.next_structural_index : 0); - tape_builder builder(doc); - return iter.walk_document(builder); - } +template +simdjson_warn_unused simdjson_inline error_code tape_builder::parse_document( + dom_parser_implementation &dom_parser, + dom::document &doc) noexcept { + dom_parser.doc = &doc; + json_iterator iter(dom_parser, STREAMING ? dom_parser.next_structural_index : 0); + tape_builder builder(doc); + return iter.walk_document(builder); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_primitive(json_iterator& iter, const uint8_t* value) noexcept { - return iter.visit_root_primitive(*this, value); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_primitive(json_iterator& iter, const uint8_t* value) noexcept { - return iter.visit_primitive(*this, value); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_object(json_iterator& iter) noexcept { - return empty_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_array(json_iterator& iter) noexcept { - return empty_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_primitive(json_iterator &iter, const uint8_t *value) noexcept { + return iter.visit_root_primitive(*this, value); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_primitive(json_iterator &iter, const uint8_t *value) noexcept { + return iter.visit_primitive(*this, value); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_object(json_iterator &iter) noexcept { + return empty_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_array(json_iterator &iter) noexcept { + return empty_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_start(json_iterator& iter) noexcept { - start_container(iter); - return SUCCESS; - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_start(json_iterator& iter) noexcept { - start_container(iter); - return SUCCESS; - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_start(json_iterator& iter) noexcept { - start_container(iter); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_start(json_iterator &iter) noexcept { + start_container(iter); + return SUCCESS; +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_start(json_iterator &iter) noexcept { + start_container(iter); + return SUCCESS; +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_start(json_iterator &iter) noexcept { + start_container(iter); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_end(json_iterator& iter) noexcept { - return end_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_end(json_iterator& iter) noexcept { - return end_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_end(json_iterator& iter) noexcept { - constexpr uint32_t start_tape_index = 0; - tape.append(start_tape_index, internal::tape_type::ROOT); - tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter), internal::tape_type::ROOT); - return SUCCESS; - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_key(json_iterator& iter, const uint8_t* key) noexcept { - return visit_string(iter, key, true); - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_end(json_iterator &iter) noexcept { + return end_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_end(json_iterator &iter) noexcept { + return end_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_end(json_iterator &iter) noexcept { + constexpr uint32_t start_tape_index = 0; + tape.append(start_tape_index, internal::tape_type::ROOT); + tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter), internal::tape_type::ROOT); + return SUCCESS; +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_key(json_iterator &iter, const uint8_t *key) noexcept { + return visit_string(iter, key, true); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::increment_count(json_iterator& iter) noexcept { - iter.dom_parser.open_containers[iter.depth].count++; // we have a key value pair in the object at parser.dom_parser.depth - 1 - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::increment_count(json_iterator &iter) noexcept { + iter.dom_parser.open_containers[iter.depth].count++; // we have a key value pair in the object at parser.dom_parser.depth - 1 + return SUCCESS; +} - simdjson_inline tape_builder::tape_builder(dom::document& doc) noexcept : tape{ doc.tape.get() }, current_string_buf_loc{ doc.string_buf.get() } {} +simdjson_inline tape_builder::tape_builder(dom::document &doc) noexcept : tape{doc.tape.get()}, current_string_buf_loc{doc.string_buf.get()} {} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_string(json_iterator& iter, const uint8_t* value, bool key) noexcept { - iter.log_value(key ? "key" : "string"); - uint8_t* dst = on_start_string(iter); - dst = stringparsing::parse_string(value + 1, dst, false); // We do not allow replacement when the escape characters are invalid. - if (dst == nullptr) { - iter.log_error("Invalid escape in string"); - return STRING_ERROR; - } - on_end_string(dst); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_string(json_iterator &iter, const uint8_t *value, bool key) noexcept { + iter.log_value(key ? "key" : "string"); + uint8_t *dst = on_start_string(iter); + dst = stringparsing::parse_string(value+1, dst, false); // We do not allow replacement when the escape characters are invalid. + if (dst == nullptr) { + iter.log_error("Invalid escape in string"); + return STRING_ERROR; + } + on_end_string(dst); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_string(json_iterator& iter, const uint8_t* value) noexcept { - return visit_string(iter, value); - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_string(json_iterator &iter, const uint8_t *value) noexcept { + return visit_string(iter, value); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_number(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("number"); - return numberparsing::parse_number(value, tape); - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_number(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("number"); + return numberparsing::parse_number(value, tape); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_number(json_iterator& iter, const uint8_t* value) noexcept { - // - // We need to make a copy to make sure that the string is space terminated. - // This is not about padding the input, which should already padded up - // to len + SIMDJSON_PADDING. However, we have no control at this stage - // on how the padding was done. What if the input string was padded with nulls? - // It is quite common for an input string to have an extra null character (C string). - // We do not want to allow 9\0 (where \0 is the null character) inside a JSON - // document, but the string "9\0" by itself is fine. So we make a copy and - // pad the input with spaces when we know that there is just one input element. - // This copy is relatively expensive, but it will almost never be called in - // practice unless you are in the strange scenario where you have many JSON - // documents made of single atoms. - // - std::unique_ptrcopy(new (std::nothrow) uint8_t[iter.remaining_len() + SIMDJSON_PADDING]); - if (copy.get() == nullptr) { return MEMALLOC; } - std::memcpy(copy.get(), value, iter.remaining_len()); - std::memset(copy.get() + iter.remaining_len(), ' ', SIMDJSON_PADDING); - error_code error = visit_number(iter, copy.get()); - return error; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_number(json_iterator &iter, const uint8_t *value) noexcept { + // + // We need to make a copy to make sure that the string is space terminated. + // This is not about padding the input, which should already padded up + // to len + SIMDJSON_PADDING. However, we have no control at this stage + // on how the padding was done. What if the input string was padded with nulls? + // It is quite common for an input string to have an extra null character (C string). + // We do not want to allow 9\0 (where \0 is the null character) inside a JSON + // document, but the string "9\0" by itself is fine. So we make a copy and + // pad the input with spaces when we know that there is just one input element. + // This copy is relatively expensive, but it will almost never be called in + // practice unless you are in the strange scenario where you have many JSON + // documents made of single atoms. + // + std::unique_ptrcopy(new (std::nothrow) uint8_t[iter.remaining_len() + SIMDJSON_PADDING]); + if (copy.get() == nullptr) { return MEMALLOC; } + std::memcpy(copy.get(), value, iter.remaining_len()); + std::memset(copy.get() + iter.remaining_len(), ' ', SIMDJSON_PADDING); + error_code error = visit_number(iter, copy.get()); + return error; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_true_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("true"); - if (!atomparsing::is_valid_true_atom(value)) { return T_ATOM_ERROR; } - tape.append(0, internal::tape_type::TRUE_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_true_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("true"); + if (!atomparsing::is_valid_true_atom(value)) { return T_ATOM_ERROR; } + tape.append(0, internal::tape_type::TRUE_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_true_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("true"); - if (!atomparsing::is_valid_true_atom(value, iter.remaining_len())) { return T_ATOM_ERROR; } - tape.append(0, internal::tape_type::TRUE_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_true_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("true"); + if (!atomparsing::is_valid_true_atom(value, iter.remaining_len())) { return T_ATOM_ERROR; } + tape.append(0, internal::tape_type::TRUE_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_false_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("false"); - if (!atomparsing::is_valid_false_atom(value)) { return F_ATOM_ERROR; } - tape.append(0, internal::tape_type::FALSE_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_false_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("false"); + if (!atomparsing::is_valid_false_atom(value)) { return F_ATOM_ERROR; } + tape.append(0, internal::tape_type::FALSE_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_false_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("false"); - if (!atomparsing::is_valid_false_atom(value, iter.remaining_len())) { return F_ATOM_ERROR; } - tape.append(0, internal::tape_type::FALSE_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("false"); + if (!atomparsing::is_valid_false_atom(value, iter.remaining_len())) { return F_ATOM_ERROR; } + tape.append(0, internal::tape_type::FALSE_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_null_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("null"); - if (!atomparsing::is_valid_null_atom(value)) { return N_ATOM_ERROR; } - tape.append(0, internal::tape_type::NULL_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_null_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("null"); + if (!atomparsing::is_valid_null_atom(value)) { return N_ATOM_ERROR; } + tape.append(0, internal::tape_type::NULL_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_null_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("null"); - if (!atomparsing::is_valid_null_atom(value, iter.remaining_len())) { return N_ATOM_ERROR; } - tape.append(0, internal::tape_type::NULL_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("null"); + if (!atomparsing::is_valid_null_atom(value, iter.remaining_len())) { return N_ATOM_ERROR; } + tape.append(0, internal::tape_type::NULL_VALUE); + return SUCCESS; +} - // private: +// private: - simdjson_inline uint32_t tape_builder::next_tape_index(json_iterator& iter) const noexcept { - return uint32_t(tape.next_tape_loc - iter.dom_parser.doc->tape.get()); - } +simdjson_inline uint32_t tape_builder::next_tape_index(json_iterator &iter) const noexcept { + return uint32_t(tape.next_tape_loc - iter.dom_parser.doc->tape.get()); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::empty_container(json_iterator& iter, internal::tape_type start, internal::tape_type end) noexcept { - auto start_index = next_tape_index(iter); - tape.append(start_index + 2, start); - tape.append(start_index, end); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::empty_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept { + auto start_index = next_tape_index(iter); + tape.append(start_index+2, start); + tape.append(start_index, end); + return SUCCESS; +} - simdjson_inline void tape_builder::start_container(json_iterator& iter) noexcept { - iter.dom_parser.open_containers[iter.depth].tape_index = next_tape_index(iter); - iter.dom_parser.open_containers[iter.depth].count = 0; - tape.skip(); // We don't actually *write* the start element until the end. - } +simdjson_inline void tape_builder::start_container(json_iterator &iter) noexcept { + iter.dom_parser.open_containers[iter.depth].tape_index = next_tape_index(iter); + iter.dom_parser.open_containers[iter.depth].count = 0; + tape.skip(); // We don't actually *write* the start element until the end. +} - simdjson_warn_unused simdjson_inline error_code tape_builder::end_container(json_iterator& iter, internal::tape_type start, internal::tape_type end) noexcept { - // Write the ending tape element, pointing at the start location - const uint32_t start_tape_index = iter.dom_parser.open_containers[iter.depth].tape_index; - tape.append(start_tape_index, end); - // Write the start tape element, pointing at the end location (and including count) - // count can overflow if it exceeds 24 bits... so we saturate - // the convention being that a cnt of 0xffffff or more is undetermined in value (>= 0xffffff). - const uint32_t count = iter.dom_parser.open_containers[iter.depth].count; - const uint32_t cntsat = count > 0xFFFFFF ? 0xFFFFFF : count; - tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter) | (uint64_t(cntsat) << 32), start); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::end_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept { + // Write the ending tape element, pointing at the start location + const uint32_t start_tape_index = iter.dom_parser.open_containers[iter.depth].tape_index; + tape.append(start_tape_index, end); + // Write the start tape element, pointing at the end location (and including count) + // count can overflow if it exceeds 24 bits... so we saturate + // the convention being that a cnt of 0xffffff or more is undetermined in value (>= 0xffffff). + const uint32_t count = iter.dom_parser.open_containers[iter.depth].count; + const uint32_t cntsat = count > 0xFFFFFF ? 0xFFFFFF : count; + tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter) | (uint64_t(cntsat) << 32), start); + return SUCCESS; +} - simdjson_inline uint8_t* tape_builder::on_start_string(json_iterator& iter) noexcept { - // we advance the point, accounting for the fact that we have a NULL termination - tape.append(current_string_buf_loc - iter.dom_parser.doc->string_buf.get(), internal::tape_type::STRING); - return current_string_buf_loc + sizeof(uint32_t); - } +simdjson_inline uint8_t *tape_builder::on_start_string(json_iterator &iter) noexcept { + // we advance the point, accounting for the fact that we have a NULL termination + tape.append(current_string_buf_loc - iter.dom_parser.doc->string_buf.get(), internal::tape_type::STRING); + return current_string_buf_loc + sizeof(uint32_t); +} - simdjson_inline void tape_builder::on_end_string(uint8_t* dst) noexcept { - uint32_t str_length = uint32_t(dst - (current_string_buf_loc + sizeof(uint32_t))); - // TODO check for overflow in case someone has a crazy string (>=4GB?) - // But only add the overflow check when the document itself exceeds 4GB - // Currently unneeded because we refuse to parse docs larger or equal to 4GB. - memcpy(current_string_buf_loc, &str_length, sizeof(uint32_t)); - // NULL termination is still handy if you expect all your strings to - // be NULL terminated? It comes at a small cost - *dst = 0; - current_string_buf_loc = dst + 1; - } +simdjson_inline void tape_builder::on_end_string(uint8_t *dst) noexcept { + uint32_t str_length = uint32_t(dst - (current_string_buf_loc + sizeof(uint32_t))); + // TODO check for overflow in case someone has a crazy string (>=4GB?) + // But only add the overflow check when the document itself exceeds 4GB + // Currently unneeded because we refuse to parse docs larger or equal to 4GB. + memcpy(current_string_buf_loc, &str_length, sizeof(uint32_t)); + // NULL termination is still handy if you expect all your strings to + // be NULL terminated? It comes at a small cost + *dst = 0; + current_string_buf_loc = dst + 1; +} - } // namespace stage2 - } // unnamed namespace - } // namespace lsx +} // namespace stage2 +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_BUILDER_H @@ -46118,69 +54187,69 @@ namespace simdjson { // Stage 1 // namespace simdjson { - namespace lsx { +namespace lsx { - simdjson_warn_unused error_code implementation::create_dom_parser_implementation( - size_t capacity, - size_t max_depth, - std::unique_ptr& dst - ) const noexcept { - dst.reset(new (std::nothrow) dom_parser_implementation()); - if (!dst) { return MEMALLOC; } - if (auto err = dst->set_capacity(capacity)) - return err; - if (auto err = dst->set_max_depth(max_depth)) - return err; - return SUCCESS; - } +simdjson_warn_unused error_code implementation::create_dom_parser_implementation( + size_t capacity, + size_t max_depth, + std::unique_ptr& dst +) const noexcept { + dst.reset( new (std::nothrow) dom_parser_implementation() ); + if (!dst) { return MEMALLOC; } + if (auto err = dst->set_capacity(capacity)) + return err; + if (auto err = dst->set_max_depth(max_depth)) + return err; + return SUCCESS; +} - namespace { +namespace { - using namespace simd; +using namespace simd; - simdjson_inline json_character_block json_character_block::classify(const simd::simd8x64& in) { - // Inspired by haswell. - // LSX use low 5 bits as index. For the 6 operators (:,[]{}), the unique-5bits is [6:2]. - // The ASCII white-space and operators have these values: (char, hex, unique-5bits) - // (' ', 20, 00000) ('\t', 09, 01001) ('\n', 0A, 01010) ('\r', 0D, 01101) - // (',', 2C, 01011) (':', 3A, 01110) ('[', 5B, 10110) ('{', 7B, 11110) (']', 5D, 10111) ('}', 7D, 11111) - const simd8 ws_table = simd8::repeat_16( - ' ', 0, 0, 0, 0, 0, 0, 0, 0, '\t', '\n', 0, 0, '\r', 0, 0 - ); - const simd8 op_table_lo = simd8::repeat_16( - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ',', 0, 0, ':', 0 - ); - const simd8 op_table_hi = simd8::repeat_16( - 0, 0, 0, 0, 0, 0, '[', ']', 0, 0, 0, 0, 0, 0, '{', '}' - ); - uint64_t ws = in.eq({ - in.chunks[0].lookup_16(ws_table), - in.chunks[1].lookup_16(ws_table), - in.chunks[2].lookup_16(ws_table), - in.chunks[3].lookup_16(ws_table) - }); - uint64_t op = in.eq({ - __lsx_vshuf_b(op_table_hi, op_table_lo, in.chunks[0].shr<2>()), - __lsx_vshuf_b(op_table_hi, op_table_lo, in.chunks[1].shr<2>()), - __lsx_vshuf_b(op_table_hi, op_table_lo, in.chunks[2].shr<2>()), - __lsx_vshuf_b(op_table_hi, op_table_lo, in.chunks[3].shr<2>()) - }); +simdjson_inline json_character_block json_character_block::classify(const simd::simd8x64& in) { + // Inspired by haswell. + // LSX use low 5 bits as index. For the 6 operators (:,[]{}), the unique-5bits is [6:2]. + // The ASCII white-space and operators have these values: (char, hex, unique-5bits) + // (' ', 20, 00000) ('\t', 09, 01001) ('\n', 0A, 01010) ('\r', 0D, 01101) + // (',', 2C, 01011) (':', 3A, 01110) ('[', 5B, 10110) ('{', 7B, 11110) (']', 5D, 10111) ('}', 7D, 11111) + const simd8 ws_table = simd8::repeat_16( + ' ', 0, 0, 0, 0, 0, 0, 0, 0, '\t', '\n', 0, 0, '\r', 0, 0 + ); + const simd8 op_table_lo = simd8::repeat_16( + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ',', 0, 0, ':', 0 + ); + const simd8 op_table_hi = simd8::repeat_16( + 0, 0, 0, 0, 0, 0, '[', ']', 0, 0, 0, 0, 0, 0, '{', '}' + ); + uint64_t ws = in.eq({ + in.chunks[0].lookup_16(ws_table), + in.chunks[1].lookup_16(ws_table), + in.chunks[2].lookup_16(ws_table), + in.chunks[3].lookup_16(ws_table) + }); + uint64_t op = in.eq({ + __lsx_vshuf_b(op_table_hi, op_table_lo, in.chunks[0].shr<2>()), + __lsx_vshuf_b(op_table_hi, op_table_lo, in.chunks[1].shr<2>()), + __lsx_vshuf_b(op_table_hi, op_table_lo, in.chunks[2].shr<2>()), + __lsx_vshuf_b(op_table_hi, op_table_lo, in.chunks[3].shr<2>()) + }); - return { ws, op }; - } + return { ws, op }; +} - simdjson_inline bool is_ascii(const simd8x64& input) { - return input.reduce_or().is_ascii(); - } +simdjson_inline bool is_ascii(const simd8x64& input) { + return input.reduce_or().is_ascii(); +} - simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { - simd8 is_third_byte = prev2.saturating_sub(0xe0u - 0x80); // Only 111_____ will be >= 0x80 - simd8 is_fourth_byte = prev3.saturating_sub(0xf0u - 0x80); // Only 1111____ will be >= 0x80 - return is_third_byte | is_fourth_byte; - } +simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { + simd8 is_third_byte = prev2.saturating_sub(0xe0u-0x80); // Only 111_____ will be >= 0x80 + simd8 is_fourth_byte = prev3.saturating_sub(0xf0u-0x80); // Only 1111____ will be >= 0x80 + return is_third_byte | is_fourth_byte; +} - } // unnamed namespace - } // namespace lsx +} // unnamed namespace +} // namespace lsx } // namespace simdjson // @@ -46191,45 +54260,46 @@ namespace simdjson { // Implementation-specific overrides // namespace simdjson { - namespace lsx { +namespace lsx { - simdjson_warn_unused error_code implementation::minify(const uint8_t* buf, size_t len, uint8_t* dst, size_t& dst_len) const noexcept { - return lsx::stage1::json_minifier::minify<64>(buf, len, dst, dst_len); - } +simdjson_warn_unused error_code implementation::minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept { + return lsx::stage1::json_minifier::minify<64>(buf, len, dst, dst_len); +} - simdjson_warn_unused error_code dom_parser_implementation::stage1(const uint8_t* _buf, size_t _len, stage1_mode streaming) noexcept { - this->buf = _buf; - this->len = _len; - return lsx::stage1::json_structural_indexer::index<64>(buf, len, *this, streaming); - } +simdjson_warn_unused error_code dom_parser_implementation::stage1(const uint8_t *_buf, size_t _len, stage1_mode streaming) noexcept { + this->buf = _buf; + this->len = _len; + return lsx::stage1::json_structural_indexer::index<64>(buf, len, *this, streaming); +} - simdjson_warn_unused bool implementation::validate_utf8(const char* buf, size_t len) const noexcept { - return lsx::stage1::generic_validate_utf8(buf, len); - } +simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept { + return lsx::stage1::generic_validate_utf8(buf,len); +} - simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document& _doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); - } +simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept { + return stage2::tape_builder::parse_document(*this, _doc); +} - simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document& _doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); - } +simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept { + return stage2::tape_builder::parse_document(*this, _doc); +} - simdjson_warn_unused uint8_t* dom_parser_implementation::parse_string(const uint8_t* src, uint8_t* dst, bool allow_replacement) const noexcept { - return lsx::stringparsing::parse_string(src, dst, allow_replacement); - } +SIMDJSON_NO_SANITIZE_MEMORY +simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept { + return lsx::stringparsing::parse_string(src, dst, allow_replacement); +} - simdjson_warn_unused uint8_t* dom_parser_implementation::parse_wobbly_string(const uint8_t* src, uint8_t* dst) const noexcept { - return lsx::stringparsing::parse_wobbly_string(src, dst); - } +simdjson_warn_unused uint8_t *dom_parser_implementation::parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept { + return lsx::stringparsing::parse_wobbly_string(src, dst); +} - simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t* _buf, size_t _len, dom::document& _doc) noexcept { - auto error = stage1(_buf, _len, stage1_mode::regular); - if (error) { return error; } - return stage2(_doc); - } +simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept { + auto error = stage1(_buf, _len, stage1_mode::regular); + if (error) { return error; } + return stage2(_doc); +} - } // namespace lsx +} // namespace lsx } // namespace simdjson /* including simdjson/lsx/end.h: #include */ @@ -46246,642 +54316,680 @@ namespace simdjson { #endif // SIMDJSON_SRC_LSX_CPP /* end file lsx.cpp */ #endif -#if SIMDJSON_IMPLEMENTATION_LASX -/* including lasx.cpp: #include */ -/* begin file lasx.cpp */ -#ifndef SIMDJSON_SRC_LASX_CPP -#define SIMDJSON_SRC_LASX_CPP +#if SIMDJSON_IMPLEMENTATION_RVV_VLS +/* including rvv-vls.cpp: #include */ +/* begin file rvv-vls.cpp */ +#ifndef SIMDJSON_SRC_RVV_VLS_CPP +#define SIMDJSON_SRC_RVV_VLS_CPP /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ /* amalgamation skipped (editor-only): #include */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ -/* including simdjson/lasx.h: #include */ -/* begin file simdjson/lasx.h */ -#ifndef SIMDJSON_LASX_H -#define SIMDJSON_LASX_H +/* including simdjson/rvv-vls.h: #include */ +/* begin file simdjson/rvv-vls.h */ +#ifndef SIMDJSON_RVV_VLS_H +#define SIMDJSON_RVV_VLS_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 -/* 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 + +/* 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 { - /** - * Implementation for LASX. - */ - namespace lasx { +/** + * RVV-VLS implementation. + */ +namespace rvv_vls { - class implementation; +class implementation; - namespace { - namespace simd { - template struct simd8; - template struct simd8x64; - } // namespace simd - } // unnamed namespace - - } // namespace lasx +} // namespace rvv_vls } // 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 +#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/lasx/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/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 +#include -static_assert(sizeof(__m256i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for LoongArch ASX"); +#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())) -#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); - } - - simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t* result) { - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); - } - - } // 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 +#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 #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 + +#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/lasx/base.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/lasx/intrinsics.h" */ +/* 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); +} + +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); +} + +} // 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 + struct simd8; + + // SIMD byte mask type (returned by things like eq and gt) + template<> + struct simd8 { + 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 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 operator|(const simd8 other) const { return __riscv_vmor(*this, other, VL8); } + simdjson_inline simd8 operator&(const simd8 other) const { return __riscv_vmand(*this, other, VL8); } + simdjson_inline simd8 operator^(const simd8 other) const { return __riscv_vmxor(*this, other, VL8); } + simdjson_inline simd8 bit_andnot(const simd8 other) const { return __riscv_vmandn(other, *this, VL8); } + simdjson_inline simd8 operator~() const { return __riscv_vmnot(*this, VL8); } + simdjson_inline simd8& operator|=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline simd8& operator&=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline simd8& operator^=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; + + // Unsigned bytes + template<> + struct simd8 { + + 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 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 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( + 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 operator|(const simd8 other) const { return __riscv_vor_vv_u8m1( value, other, VL8); } + simdjson_inline simd8 operator&(const simd8 other) const { return __riscv_vand_vv_u8m1( value, other, VL8); } + simdjson_inline simd8 operator^(const simd8 other) const { return __riscv_vxor_vv_u8m1( value, other, VL8); } + simdjson_inline simd8 operator~() const { return __riscv_vnot_v_u8m1(value, VL8); } +#if __riscv_zvbb + simdjson_inline simd8 bit_andnot(const simd8 other) const { return __riscv_vandn_vv_u8m1(other, value, VL8); } +#else + simdjson_inline simd8 bit_andnot(const simd8 other) const { return other & ~*this; } +#endif + simdjson_inline simd8& operator|=(const simd8 other) { value = *this | other; return *this; } + simdjson_inline simd8& operator&=(const simd8 other) { value = *this & other; return *this; } + simdjson_inline simd8& operator^=(const simd8 other) { value = *this ^ other; return *this; } + + simdjson_inline simd8 operator==(const simd8 other) const { return __riscv_vmseq(value, other, VL8); } + simdjson_inline simd8 operator==(uint8_t other) const { return __riscv_vmseq(value, other, VL8); } + + template + simdjson_inline simd8 prev(const simd8 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 saturating_add(const simd8 other) const { return __riscv_vsaddu(value, other, VL8); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return __riscv_vssubu(value, other, VL8); } + + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { return __riscv_vadd(value, other, VL8); } + simdjson_inline simd8 operator-(const simd8 other) const { return __riscv_vsub(value, other, VL8); } + simdjson_inline simd8& operator+=(const simd8 other) { value = *this + other; return *this; } + simdjson_inline simd8& operator-=(const simd8 other) { value = *this - other; return *this; } + + // Order-specific operations + simdjson_inline simd8 operator<=(const simd8 other) const { return __riscv_vmsleu(value, other, VL8); } + simdjson_inline simd8 operator>=(const simd8 other) const { return __riscv_vmsgeu(value, other, VL8); } + simdjson_inline simd8 operator<(const simd8 other) const { return __riscv_vmsltu(value, other, VL8); } + simdjson_inline simd8 operator>(const simd8 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 gt_bits(const simd8 other) const { return simd8(*this > other); } + // Same as <, but instead of guaranteeing all 1's == true, false = 0 and true = nonzero. + simdjson_inline simd8 lt_bits(const simd8 other) const { return simd8(*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 bits) const { return (*this & bits).any_bits_set_anywhere(); } + template + simdjson_inline simd8 shr() const { return __riscv_vsrl(value, N, VL8); } + template + simdjson_inline simd8 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 + simdjson_inline simd8 lookup_16(simd8 lookup_table) const { + return __riscv_vrgather(lookup_table, value, VL8); + } + + // compress inactive elements, to match AVX-512 behavior + template + 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 + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; + + // Signed bytes + template<> + struct simd8 { + 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 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( + 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() const { return __riscv_vreinterpret_u8m1(value); } + + // Math + simdjson_inline simd8 operator+(const simd8 other) const { return __riscv_vadd(value, other, VL8); } + simdjson_inline simd8 operator-(const simd8 other) const { return __riscv_vsub(value, other, VL8); } + simdjson_inline simd8& operator+=(const simd8 other) { value = *this + other; return *this; } + simdjson_inline simd8& operator-=(const simd8 other) { value = *this - other; return *this; } + + // Order-sensitive comparisons + simdjson_inline simd8 max_val( const simd8 other) const { return __riscv_vmax( value, other, VL8); } + simdjson_inline simd8 min_val( const simd8 other) const { return __riscv_vmin( value, other, VL8); } + simdjson_inline simd8 operator>( const simd8 other) const { return __riscv_vmsgt(value, other, VL8); } + simdjson_inline simd8 operator<( const simd8 other) const { return __riscv_vmslt(value, other, VL8); } + simdjson_inline simd8 operator==(const simd8 other) const { return __riscv_vmseq(value, other, VL8); } + + template + simdjson_inline simd8 prev(const simd8 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 + simdjson_inline simd8 lookup_16(simd8 lookup_table) const { + return __riscv_vrgather(lookup_table, value, VL8); + } + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; + + template + struct simd8x64; + template<> + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + vuint8x64_t value; + +#if __riscv_v_fixed_vlen >= 512 + template simd8 get() const { return value; } +#else + template simd8 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& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& 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 + +} // 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); + 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 v(src); + v.store(dst); + return { (v == '\\').to_bitmask(), (v == '"').to_bitmask() }; +} + +struct escaping { + static constexpr uint64_t BYTES_PROCESSED = sizeof(simd8); + 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 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 +#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 lasx { - namespace numberparsing { +namespace rvv_vls { +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); - } +// 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); +} - 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; - } +/** @private */ +static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) { + return parse_eight_digits_unrolled(reinterpret_cast(chars)); +} - } // namespace numberparsing - } // namespace lasx +/** @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_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 - 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(this); *this_cast = *this_cast | other; return *this_cast; } - simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } - simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } - }; - - // Forward-declared so they can be used by splat and friends. - template - struct simd8; - - template> - struct base8 : base> { - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m256i _value) : base>(_value) {} - - friend simdjson_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return __lasx_xvseq_b(lhs, rhs); } - - static const int SIZE = sizeof(base>::value); - - template - simdjson_inline simd8 prev(const simd8 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 : base8 { - static simdjson_inline simd8 splat(bool _value) { return __lasx_xvreplgr2vr_b(uint8_t(-(!!_value))); } - - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m256i _value) : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) : base8(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 operator~() const { return *this ^ true; } - }; - - template - struct base8_numeric : base8 { - static simdjson_inline simd8 splat(T _value) { - return __lasx_xvreplgr2vr_b(_value); - } - static simdjson_inline simd8 zero() { return __lasx_xvldi(0); } - static simdjson_inline simd8 load(const T values[32]) { - return __lasx_xvld(reinterpret_cast(values), 0); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdjson_inline simd8 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( - 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() {} - simdjson_inline base8_numeric(const __m256i _value) : base8(_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 operator+(const simd8 other) const { return __lasx_xvadd_b(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return __lasx_xvsub_b(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } - - // Override to distinguish from bool version - simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 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 - 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(reinterpret_cast(pshufb_combine_table)), pop1 * 8); - __m256i maskhi = __lasx_xvldx(reinterpret_cast(reinterpret_cast(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(output), 0); - uint64_t value3 = __lasx_xvpickve2gr_du(answer, 2); - uint64_t value4 = __lasx_xvpickve2gr_du(answer, 3); - uint64_t* pos = reinterpret_cast(reinterpret_cast(output) + 16 - (pop1 + pop2) / 2); - pos[0] = value3; - pos[1] = value4; - } - - template - simdjson_inline simd8 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::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - }; - - // Signed bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( - 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 max_val(const simd8 other) const { return __lasx_xvmax_b(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return __lasx_xvmin_b(*this, other); } - simdjson_inline simd8 operator>(const simd8 other) const { return __lasx_xvslt_b(other, *this); } - simdjson_inline simd8 operator<(const simd8 other) const { return __lasx_xvslt_b(*this, other); } - }; - - // Unsigned bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( - 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 saturating_add(const simd8 other) const { return __lasx_xvsadd_bu(*this, other); } - simdjson_inline simd8 saturating_sub(const simd8 other) const { return __lasx_xvssub_bu(*this, other); } - - // Order-specific operations - simdjson_inline simd8 max_val(const simd8 other) const { return __lasx_xvmax_bu(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return __lasx_xvmin_bu(other, *this); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } - simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } - simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } - simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } - - // Bit-specific operations - simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } - simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } - simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } - simdjson_inline simd8 any_bits_set(simd8 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 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 bits) const { return !bits_not_set_anywhere(bits); } - template - simdjson_inline simd8 shr() const { return simd8(__lasx_xvsrli_b(*this, N)); } - template - simdjson_inline simd8 shl() const { return simd8(__lasx_xvslli_b(*this, N)); } - }; - - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 2, "LASX kernel should use two registers per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; - - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed - - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1) : chunks{ chunk0, chunk1 } {} - simdjson_inline simd8x64(const T ptr[64]) : chunks{ simd8::load(ptr), simd8::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) * 0); - this->chunks[1].store(ptr + sizeof(simd8) * 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 reduce_or() const { - return this->chunks[0] | this->chunks[1]; - } - - simdjson_inline uint64_t eq(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] == mask, - this->chunks[1] == mask - ).to_bitmask(); - } - - simdjson_inline uint64_t eq(const simd8x64& other) const { - return simd8x64( - 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] <= mask, - this->chunks[1] <= mask - ).to_bitmask(); - } - }; // struct simd8x64 - - } // 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 static 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 v(src); - v.store(dst); - return { - static_cast((v == '\\').to_bitmask()), // bs_bits - static_cast((v == '"').to_bitmask()), // quote_bits - }; - } - - } // unnamed namespace - } // namespace lasx -} // namespace simdjson - -#endif // SIMDJSON_LASX_STRINGPARSING_DEFS_H -/* end file simdjson/lasx/stringparsing_defs.h */ +#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/lasx/begin.h */ -/* including simdjson/generic/amalgamated.h for lasx: #include "simdjson/generic/amalgamated.h" */ -/* begin file simdjson/generic/amalgamated.h for lasx */ +/* 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 lasx: #include "simdjson/generic/base.h" */ -/* begin file simdjson/generic/base.h for lasx */ +/* 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 */ @@ -46901,10 +55009,12 @@ namespace simdjson { /* 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_LSX */ -/* amalgamation skipped (editor-only): #include "simdjson/lsx/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 */ @@ -46914,28 +55024,28 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { +namespace rvv_vls { - struct open_container; - class dom_parser_implementation; +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 - }; +/** + * 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 rvv_vls } // 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 */ +/* 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 */ @@ -46946,105 +55056,103 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace { - namespace jsoncharutils { +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]; - } +// 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]; - } +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; - } +// 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 - } +// 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; - } +// 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 jsoncharutils +} // unnamed namespace +} // namespace rvv_vls } // 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 */ +/* 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 */ @@ -47056,75 +55164,75 @@ namespace simdjson { #include namespace simdjson { - namespace lasx { - namespace { - /// @private - namespace atomparsing { +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; } +// 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; } - // 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); - } +// 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); +} - 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) { + 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_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) { + 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_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) { + 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; } - } +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; } +} - } // namespace atomparsing - } // unnamed namespace - } // namespace lasx +} // namespace atomparsing +} // unnamed namespace +} // namespace rvv_vls } // 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 */ +/* 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 */ @@ -47134,89 +55242,89 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { +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 +// 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"); +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_containers{}; - /** Whether each open container is a [ or { */ - std::unique_ptr 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{}; +class dom_parser_implementation final : public internal::dom_parser_implementation { +public: + /** Tape location of each open { or [ */ + std::unique_ptr open_containers{}; + /** Whether each open container is a [ or { */ + std::unique_ptr 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; + 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); + 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 rvv_vls } // namespace simdjson namespace simdjson { - namespace lasx { +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; +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) { return CAPACITY; } - // Stage 1 index output - size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; - 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; +// 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) { return CAPACITY; } + // Stage 1 index output + size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; + 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; - } + _capacity = capacity; + return SUCCESS; +} - inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { - // 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; } +inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { + // 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; - } + _max_depth = max_depth; + return SUCCESS; +} - } // namespace lasx +} // namespace rvv_vls } // 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 */ +/* 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 */ @@ -47225,135 +55333,154 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { +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 : public internal::implementation_simdjson_result_base { - * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} - * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} - * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} - * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} - * // Your extra methods here - * } - * - * Then any method returning simdjson_result will be chainable with your methods. - */ - template - struct implementation_simdjson_result_base { +// 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 : public internal::implementation_simdjson_result_base { + * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} + * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} + * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} + * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} + * // Your extra methods here + * } + * + * Then any method returning simdjson_result will be chainable with your methods. + */ +template +struct implementation_simdjson_result_base { - /** - * Create a new empty result with error = UNINITIALIZED. - */ - simdjson_inline implementation_simdjson_result_base() noexcept = default; + /** + * 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 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 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; + /** + * 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 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; + /** + * 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_inline error_code error() const 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& value() & noexcept(false); + /** + * 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); - /** - * Take the result value (move it). - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline T&& value() && 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&& take_value() && noexcept(false); + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& 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); + /** + * 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; - 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 + /** + * 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; - } // namespace lasx + 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 lasx */ -/* including simdjson/generic/numberparsing.h for lasx: #include "simdjson/generic/numberparsing.h" */ -/* begin file simdjson/generic/numberparsing.h for lasx */ +/* 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 */ @@ -47368,8 +55495,8 @@ namespace simdjson { #include namespace simdjson { - namespace lasx { - namespace numberparsing { +namespace rvv_vls { +namespace numberparsing { #ifdef JSON_TEST_NUMBERS #define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) @@ -47385,540 +55512,547 @@ namespace simdjson { #define BIGINT_NUMBER(SRC) (BIGINT_ERROR) #endif - namespace { +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(negative)) << 63); - std::memcpy(&d, &mantissa, sizeof(d)); - return d; - } +// 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(negative)) << 63); + std::memcpy(&d, &mantissa, sizeof(d)); + return d; +} - // 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 +// 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) + if (0 <= power && power <= 22 && i <= 9007199254740991) #else - if (-22 <= power && power <= 22 && i <= 9007199254740991) + 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 + { + // 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. + // 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; - } + // 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 - // approximatively 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; + // 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 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. - simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]); - // 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. + // 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 - // 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. - simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); - 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); + // 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. - // 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 - } - } + // 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); - mantissa += mantissa & 1; - mantissa >>= 1; + // 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 + } + } - // 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; - } + mantissa += mantissa & 1; + mantissa >>= 1; - // 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(ptr)); - // We do not accept infinite values. + // 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; +} - // 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::max)() || *outDouble < std::numeric_limits::lowest()); - } +// 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(ptr)); + // We do not accept infinite values. - static bool parse_float_fallback(const uint8_t* ptr, const uint8_t* end_ptr, double* outDouble) { - *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(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::max)() || *outDouble < std::numeric_limits::lowest()); +} - // 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::max)() || *outDouble < std::numeric_limits::lowest()); - } +static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) { + *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(end_ptr)); + // We do not accept infinite values. - // 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); - } + // 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::max)() || *outDouble < std::numeric_limits::lowest()); +} - template - 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(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; - } +// 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); +} - simdjson_inline bool is_digit(const uint8_t c) { - return static_cast(c - '0') <= 9; - } +template +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(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 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; +simdjson_inline bool is_digit(const uint8_t c) { + return static_cast(c - '0') <= 9; +} + +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; #ifdef SIMDJSON_SWAR_NUMBER_PARSING #if SIMDJSON_SWAR_NUMBER_PARSING - // this helps if we have lots of decimals! - // this turns out to be frequent enough. - if (is_made_of_eight_digits_fast(p)) { - i = i * 100000000 + parse_eight_digits_unrolled(p); - p += 8; - } + // this helps if we have lots of decimals! + // this turns out to be frequent enough. + if (is_made_of_eight_digits_fast(p)) { + i = i * 100000000 + parse_eight_digits_unrolled(p); + p += 8; + } #endif // SIMDJSON_SWAR_NUMBER_PARSING #endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING - // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) - if (parse_digit(*p, i)) { ++p; } - while (parse_digit(*p, i)) { p++; } - exponent = first_after_period - p; - // Decimal without digits (123.) is illegal - if (exponent == 0) { - return INVALID_NUMBER(src); - } - return SUCCESS; - } + // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) + if (parse_digit(*p, i)) { ++p; } + while (parse_digit(*p, i)) { p++; } + exponent = first_after_period - p; + // Decimal without digits (123.) is illegal + if (exponent == 0) { + return INVALID_NUMBER(src); + } + return SUCCESS; +} - 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 +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. + // 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. + // 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 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; - } + // 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 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); - } +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 +} // unnamed namespace - /** @private */ - static 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 */ +static 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 - 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; - } +/** @private */ +template +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 - simdjson_inline error_code parse_number(const uint8_t* const src, W& writer); +// 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 +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 +// for performance analysis, it is sometimes useful to skip parsing #ifdef SIMDJSON_SKIPNUMBERPARSING - template - simdjson_inline error_code parse_number(const uint8_t* const, W& writer) { - writer.append_s64(0); // always write zero - return SUCCESS; // always succeeds - } +template +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 parse_unsigned(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline bool is_negative(const uint8_t* src) noexcept { return false; } - simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t* src) noexcept { return false; } - simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t* src) noexcept { return number_type::signed_integer; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t * src) noexcept { return number_type::signed_integer; } #else // parse the number at src @@ -47930,751 +56064,760 @@ namespace simdjson { // content and append a space before calling this function. // // Our objective is accurate parsing (ULP of 0) at high speed. - template - 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)) { 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 INVALID_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 { - WRITE_INTEGER(negative ? (~i + 1) : i, src, writer); - } - 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 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; - 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); - // 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 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 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; - 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); - // 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 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; - 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); - // 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 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 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; - while (parse_digit(*src, i)) { src++; } - - // 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 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; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (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 == '.')) { - p++; - const uint8_t* start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (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 = 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; - } - - simdjson_unused simdjson_inline bool is_negative(const uint8_t* src) noexcept { - return (*src == '-'); - } - - simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t* src) noexcept { - bool negative = (*src == '-'); - src += uint8_t(negative); - const uint8_t* p = src; - while (static_cast(*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 get_number_type(const uint8_t* src) noexcept { - bool negative = (*src == '-'); - src += uint8_t(negative); - const uint8_t* p = src; - while (static_cast(*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("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; - } - 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("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 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 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; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (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 == '.')) { - p++; - const uint8_t* start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (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 = 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; - } - - } // unnamed namespace +template +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)) { 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 INVALID_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 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; + 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); + // 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 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 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; + 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); + // 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 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; + 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); + // 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 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 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; + while (parse_digit(*src, i)) { src++; } + + // 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { + return (*src == '-'); +} + +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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 get_number_type(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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("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("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 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +} // unnamed namespace #endif // SIMDJSON_SKIPNUMBERPARSING - } // namespace numberparsing +} // 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; - } +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 rvv_vls } // namespace simdjson #endif // SIMDJSON_GENERIC_NUMBERPARSING_H -/* end file simdjson/generic/numberparsing.h for lasx */ +/* end file simdjson/generic/numberparsing.h for rvv_vls */ -/* 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 */ +/* 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 */ @@ -48684,758 +56827,1199 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { +namespace rvv_vls { - // - // internal::implementation_simdjson_result_base inline implementation - // +// +// internal::implementation_simdjson_result_base inline implementation +// - template - simdjson_inline void implementation_simdjson_result_base::tie(T& value, error_code& error) && noexcept { - error = this->second; - if (!error) { - value = std::forward>(*this).first; - } - } +template +simdjson_inline void implementation_simdjson_result_base::tie(T &value, error_code &error) && noexcept { + error = this->second; + if (!error) { + value = std::forward>(*this).first; + } +} - template - simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T& value) && noexcept { - error_code error; - std::forward>(*this).tie(value, error); - return error; - } +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T &value) && noexcept { + error_code error; + std::forward>(*this).tie(value, error); + return error; +} - template - simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { - return this->second; - } +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { + return this->second; +} + + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} #if SIMDJSON_EXCEPTIONS - template - simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return this->first; - } +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} - template - simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { - return std::forward>(*this).take_value(); - } +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} - template - simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return std::forward(this->first); - } +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} - template - simdjson_inline implementation_simdjson_result_base::operator T && () && noexcept(false) { - return std::forward>(*this).take_value(); - } + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + +template +simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return this->first; +} + +template +simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { + return std::forward>(*this).take_value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return std::forward(this->first); +} + +template +simdjson_inline implementation_simdjson_result_base::operator T&&() && noexcept(false) { + return std::forward>(*this).take_value(); +} #endif // SIMDJSON_EXCEPTIONS - template - simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { - return this->first; - } +template +simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { + return this->first; +} - template - simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { - return this->first; - } +template +simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { + return this->first; +} - template - simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { - return std::forward(this->first); - } +template +simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { + return std::forward(this->first); +} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T&& value, error_code error) noexcept - : first{ std::forward(value) }, second{ error } {} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept - : implementation_simdjson_result_base(T{}, error) {} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T&& value) noexcept - : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value, error_code error) noexcept + : first{std::forward(value)}, second{error} {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept + : implementation_simdjson_result_base(T{}, error) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value) noexcept + : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} - } // namespace lasx +} // 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 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 */ +/* 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/lasx/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/base.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ -#undef SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT -/* undefining SIMDJSON_IMPLEMENTATION from "lasx" */ +/* undefining SIMDJSON_IMPLEMENTATION from "rvv_vls" */ #undef SIMDJSON_IMPLEMENTATION -/* end file simdjson/lasx/end.h */ +/* end file simdjson/rvv-vls/end.h */ -#endif // SIMDJSON_LASX_H -/* end file simdjson/lasx.h */ -/* including simdjson/lasx/implementation.h: #include */ -/* begin file simdjson/lasx/implementation.h */ -#ifndef SIMDJSON_LASX_IMPLEMENTATION_H -#define SIMDJSON_LASX_IMPLEMENTATION_H +#endif // SIMDJSON_RVV_VLS_H +/* end file simdjson/rvv-vls.h */ +/* including simdjson/rvv-vls/implementation.h: #include */ +/* 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/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/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 { +namespace rvv_vls { - /** - * @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& 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; - }; +/** + * @private + */ +class implementation final : public simdjson::implementation { +public: + simdjson_inline implementation() : simdjson::implementation( + "rvv_vls", + "RISC-V V extension", + 0 + ) {} + simdjson_warn_unused error_code create_dom_parser_implementation( + size_t capacity, + size_t max_length, + std::unique_ptr& 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 rvv_vls } // namespace simdjson -#endif // SIMDJSON_LASX_IMPLEMENTATION_H -/* end file simdjson/lasx/implementation.h */ +#endif // SIMDJSON_RVV_VLS_IMPLEMENTATION_H +/* end file simdjson/rvv-vls/implementation.h */ -/* including simdjson/lasx/begin.h: #include */ -/* begin file simdjson/lasx/begin.h */ -/* defining SIMDJSON_IMPLEMENTATION to "lasx" */ -#define SIMDJSON_IMPLEMENTATION lasx -/* 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 +/* including simdjson/rvv-vls/begin.h: #include */ +/* 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 { - /** - * Implementation for LASX. - */ - namespace lasx { +/** + * RVV-VLS implementation. + */ +namespace rvv_vls { - class implementation; +class implementation; - namespace { - namespace simd { - template struct simd8; - template struct simd8x64; - } // namespace simd - } // unnamed namespace - - } // namespace lasx +} // namespace rvv_vls } // 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 +#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/lasx/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/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 +#include -static_assert(sizeof(__m256i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for LoongArch ASX"); +#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())) -#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); - } - - simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t* result) { - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); - } - - } // 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 +#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 #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 + +#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/lasx/base.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/lasx/intrinsics.h" */ +/* 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); +} + +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); +} + +} // 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 + struct simd8; + + // SIMD byte mask type (returned by things like eq and gt) + template<> + struct simd8 { + 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 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 operator|(const simd8 other) const { return __riscv_vmor(*this, other, VL8); } + simdjson_inline simd8 operator&(const simd8 other) const { return __riscv_vmand(*this, other, VL8); } + simdjson_inline simd8 operator^(const simd8 other) const { return __riscv_vmxor(*this, other, VL8); } + simdjson_inline simd8 bit_andnot(const simd8 other) const { return __riscv_vmandn(other, *this, VL8); } + simdjson_inline simd8 operator~() const { return __riscv_vmnot(*this, VL8); } + simdjson_inline simd8& operator|=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline simd8& operator&=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline simd8& operator^=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; + + // Unsigned bytes + template<> + struct simd8 { + + 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 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 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( + 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 operator|(const simd8 other) const { return __riscv_vor_vv_u8m1( value, other, VL8); } + simdjson_inline simd8 operator&(const simd8 other) const { return __riscv_vand_vv_u8m1( value, other, VL8); } + simdjson_inline simd8 operator^(const simd8 other) const { return __riscv_vxor_vv_u8m1( value, other, VL8); } + simdjson_inline simd8 operator~() const { return __riscv_vnot_v_u8m1(value, VL8); } +#if __riscv_zvbb + simdjson_inline simd8 bit_andnot(const simd8 other) const { return __riscv_vandn_vv_u8m1(other, value, VL8); } +#else + simdjson_inline simd8 bit_andnot(const simd8 other) const { return other & ~*this; } +#endif + simdjson_inline simd8& operator|=(const simd8 other) { value = *this | other; return *this; } + simdjson_inline simd8& operator&=(const simd8 other) { value = *this & other; return *this; } + simdjson_inline simd8& operator^=(const simd8 other) { value = *this ^ other; return *this; } + + simdjson_inline simd8 operator==(const simd8 other) const { return __riscv_vmseq(value, other, VL8); } + simdjson_inline simd8 operator==(uint8_t other) const { return __riscv_vmseq(value, other, VL8); } + + template + simdjson_inline simd8 prev(const simd8 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 saturating_add(const simd8 other) const { return __riscv_vsaddu(value, other, VL8); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return __riscv_vssubu(value, other, VL8); } + + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { return __riscv_vadd(value, other, VL8); } + simdjson_inline simd8 operator-(const simd8 other) const { return __riscv_vsub(value, other, VL8); } + simdjson_inline simd8& operator+=(const simd8 other) { value = *this + other; return *this; } + simdjson_inline simd8& operator-=(const simd8 other) { value = *this - other; return *this; } + + // Order-specific operations + simdjson_inline simd8 operator<=(const simd8 other) const { return __riscv_vmsleu(value, other, VL8); } + simdjson_inline simd8 operator>=(const simd8 other) const { return __riscv_vmsgeu(value, other, VL8); } + simdjson_inline simd8 operator<(const simd8 other) const { return __riscv_vmsltu(value, other, VL8); } + simdjson_inline simd8 operator>(const simd8 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 gt_bits(const simd8 other) const { return simd8(*this > other); } + // Same as <, but instead of guaranteeing all 1's == true, false = 0 and true = nonzero. + simdjson_inline simd8 lt_bits(const simd8 other) const { return simd8(*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 bits) const { return (*this & bits).any_bits_set_anywhere(); } + template + simdjson_inline simd8 shr() const { return __riscv_vsrl(value, N, VL8); } + template + simdjson_inline simd8 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 + simdjson_inline simd8 lookup_16(simd8 lookup_table) const { + return __riscv_vrgather(lookup_table, value, VL8); + } + + // compress inactive elements, to match AVX-512 behavior + template + 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 + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; + + // Signed bytes + template<> + struct simd8 { + 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 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( + 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() const { return __riscv_vreinterpret_u8m1(value); } + + // Math + simdjson_inline simd8 operator+(const simd8 other) const { return __riscv_vadd(value, other, VL8); } + simdjson_inline simd8 operator-(const simd8 other) const { return __riscv_vsub(value, other, VL8); } + simdjson_inline simd8& operator+=(const simd8 other) { value = *this + other; return *this; } + simdjson_inline simd8& operator-=(const simd8 other) { value = *this - other; return *this; } + + // Order-sensitive comparisons + simdjson_inline simd8 max_val( const simd8 other) const { return __riscv_vmax( value, other, VL8); } + simdjson_inline simd8 min_val( const simd8 other) const { return __riscv_vmin( value, other, VL8); } + simdjson_inline simd8 operator>( const simd8 other) const { return __riscv_vmsgt(value, other, VL8); } + simdjson_inline simd8 operator<( const simd8 other) const { return __riscv_vmslt(value, other, VL8); } + simdjson_inline simd8 operator==(const simd8 other) const { return __riscv_vmseq(value, other, VL8); } + + template + simdjson_inline simd8 prev(const simd8 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 + simdjson_inline simd8 lookup_16(simd8 lookup_table) const { + return __riscv_vrgather(lookup_table, value, VL8); + } + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; + + template + struct simd8x64; + template<> + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + vuint8x64_t value; + +#if __riscv_v_fixed_vlen >= 512 + template simd8 get() const { return value; } +#else + template simd8 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& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& 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 + +} // 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); + 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 v(src); + v.store(dst); + return { (v == '\\').to_bitmask(), (v == '"').to_bitmask() }; +} + +struct escaping { + static constexpr uint64_t BYTES_PROCESSED = sizeof(simd8); + 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 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 +#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 lasx { - namespace numberparsing { +namespace rvv_vls { +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); - } +// 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); +} - 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; - } +/** @private */ +static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) { + return parse_eight_digits_unrolled(reinterpret_cast(chars)); +} - } // namespace numberparsing - } // namespace lasx +/** @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_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 +#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/rvv-vls/simd.h: #include */ +/* 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/lasx/base.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/lasx/bitmanipulation.h" */ +/* 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 lasx { - namespace { - namespace simd { +namespace rvv_vls { +namespace { +namespace simd { - // Forward-declared so they can be used by splat and friends. - template - struct base { - __m256i value; +#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 - // Zero constructor - simdjson_inline base() : value{ __m256i() } {} +#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 - // Conversion from SIMD register - simdjson_inline base(const __m256i _value) : value(_value) {} + template + struct simd8; - // 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; } + // SIMD byte mask type (returned by things like eq and gt) + template<> + struct simd8 { + vbool_t value; + using bitmask_t = vbitmask_t; + static constexpr int SIZE = sizeof(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(this); *this_cast = *this_cast | other; return *this_cast; } - simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } - simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } - }; + 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)) {} - // Forward-declared so they can be used by splat and friends. - template - struct simd8; + simdjson_inline operator const vbool_t&() const { return value; } + simdjson_inline operator vbool_t&() { return value; } - template> - struct base8 : base> { - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m256i _value) : base>(_value) {} + static simdjson_inline simd8 splat(bool _value) { + return __riscv_vreinterpret_b8(__riscv_vmv_v_x_u64m1(((uint64_t)!_value)-1, 1)); + } - friend simdjson_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return __lasx_xvseq_b(lhs, rhs); } + 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 + } - static const int SIZE = sizeof(base>::value); + // Bit operations + simdjson_inline simd8 operator|(const simd8 other) const { return __riscv_vmor(*this, other, VL8); } + simdjson_inline simd8 operator&(const simd8 other) const { return __riscv_vmand(*this, other, VL8); } + simdjson_inline simd8 operator^(const simd8 other) const { return __riscv_vmxor(*this, other, VL8); } + simdjson_inline simd8 bit_andnot(const simd8 other) const { return __riscv_vmandn(other, *this, VL8); } + simdjson_inline simd8 operator~() const { return __riscv_vmnot(*this, VL8); } + simdjson_inline simd8& operator|=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline simd8& operator&=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline simd8& operator^=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; - template - simdjson_inline simd8 prev(const simd8 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); - } - }; + // Unsigned bytes + template<> + struct simd8 { - // SIMD byte mask type (returned by things like eq and gt) - template<> - struct simd8 : base8 { - static simdjson_inline simd8 splat(bool _value) { return __lasx_xvreplgr2vr_b(uint8_t(-(!!_value))); } + vuint8_t value; + static constexpr int SIZE = sizeof(value); - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m256i _value) : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) : base8(splat(_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 mask) : value(__riscv_vmerge_vxm_u8m1(zero(), -1, (vbool_t)mask, VL8)) {} - 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 operator~() const { return *this ^ true; } - }; + simdjson_inline operator const vuint8_t&() const { return this->value; } + simdjson_inline operator vuint8_t&() { return this->value; } - template - struct base8_numeric : base8 { - static simdjson_inline simd8 splat(T _value) { - return __lasx_xvreplgr2vr_b(_value); - } - static simdjson_inline simd8 zero() { return __lasx_xvldi(0); } - static simdjson_inline simd8 load(const T values[32]) { - return __lasx_xvld(reinterpret_cast(values), 0); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdjson_inline simd8 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( - 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 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 + }) {} - simdjson_inline base8_numeric() : base8() {} - simdjson_inline base8_numeric(const __m256i _value) : base8(_value) {} + // Repeat 16 values as many times as necessary (usually for lookup tables) + simdjson_inline static simd8 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - // Store to array - simdjson_inline void store(T dst[32]) const { - return __lasx_xvst(*this, reinterpret_cast<__m256i*>(dst), 0); - } + 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); } - // Addition/subtraction are the same for signed and unsigned - simdjson_inline simd8 operator+(const simd8 other) const { return __lasx_xvadd_b(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return __lasx_xvsub_b(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } + // Bit operations + simdjson_inline simd8 operator|(const simd8 other) const { return __riscv_vor_vv_u8m1( value, other, VL8); } + simdjson_inline simd8 operator&(const simd8 other) const { return __riscv_vand_vv_u8m1( value, other, VL8); } + simdjson_inline simd8 operator^(const simd8 other) const { return __riscv_vxor_vv_u8m1( value, other, VL8); } + simdjson_inline simd8 operator~() const { return __riscv_vnot_v_u8m1(value, VL8); } +#if __riscv_zvbb + simdjson_inline simd8 bit_andnot(const simd8 other) const { return __riscv_vandn_vv_u8m1(other, value, VL8); } +#else + simdjson_inline simd8 bit_andnot(const simd8 other) const { return other & ~*this; } +#endif + simdjson_inline simd8& operator|=(const simd8 other) { value = *this | other; return *this; } + simdjson_inline simd8& operator&=(const simd8 other) { value = *this & other; return *this; } + simdjson_inline simd8& operator^=(const simd8 other) { value = *this ^ other; return *this; } - // Override to distinguish from bool version - simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } + simdjson_inline simd8 operator==(const simd8 other) const { return __riscv_vmseq(value, other, VL8); } + simdjson_inline simd8 operator==(uint8_t other) const { return __riscv_vmseq(value, other, VL8); } - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return __lasx_xvshuf_b(lookup_table, lookup_table, *this); - } + template + simdjson_inline simd8 prev(const simd8 prev_chunk) const { + return __riscv_vslideup(__riscv_vslidedown(prev_chunk, VL8-N, VL8), value, N, VL8); + } - // 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 - 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]; + // Store to array + simdjson_inline void store(uint8_t dst[VL8]) const { return __riscv_vse8(dst, value, VL8); } - // then load the corresponding mask - __m256i masklo = __lasx_xvldx(reinterpret_cast(reinterpret_cast(pshufb_combine_table)), pop1 * 8); - __m256i maskhi = __lasx_xvldx(reinterpret_cast(reinterpret_cast(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(output), 0); - uint64_t value3 = __lasx_xvpickve2gr_du(answer, 2); - uint64_t value4 = __lasx_xvpickve2gr_du(answer, 3); - uint64_t* pos = reinterpret_cast(reinterpret_cast(output) + 16 - (pop1 + pop2) / 2); - pos[0] = value3; - pos[1] = value4; - } + // Saturated math + simdjson_inline simd8 saturating_add(const simd8 other) const { return __riscv_vsaddu(value, other, VL8); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return __riscv_vssubu(value, other, VL8); } - template - simdjson_inline simd8 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::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - }; + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { return __riscv_vadd(value, other, VL8); } + simdjson_inline simd8 operator-(const simd8 other) const { return __riscv_vsub(value, other, VL8); } + simdjson_inline simd8& operator+=(const simd8 other) { value = *this + other; return *this; } + simdjson_inline simd8& operator-=(const simd8 other) { value = *this - other; return *this; } - // Signed bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( - 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-specific operations + simdjson_inline simd8 operator<=(const simd8 other) const { return __riscv_vmsleu(value, other, VL8); } + simdjson_inline simd8 operator>=(const simd8 other) const { return __riscv_vmsgeu(value, other, VL8); } + simdjson_inline simd8 operator<(const simd8 other) const { return __riscv_vmsltu(value, other, VL8); } + simdjson_inline simd8 operator>(const simd8 other) const { return __riscv_vmsgtu(value, other, VL8); } - // Order-sensitive comparisons - simdjson_inline simd8 max_val(const simd8 other) const { return __lasx_xvmax_b(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return __lasx_xvmin_b(*this, other); } - simdjson_inline simd8 operator>(const simd8 other) const { return __lasx_xvslt_b(other, *this); } - simdjson_inline simd8 operator<(const simd8 other) const { return __lasx_xvslt_b(*this, other); } - }; + // Same as >, but instead of guaranteeing all 1's == true, false = 0 and true = nonzero. + simdjson_inline simd8 gt_bits(const simd8 other) const { return simd8(*this > other); } + // Same as <, but instead of guaranteeing all 1's == true, false = 0 and true = nonzero. + simdjson_inline simd8 lt_bits(const simd8 other) const { return simd8(*this < other); } - // Unsigned bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( - 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 - ); - } + // 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 bits) const { return (*this & bits).any_bits_set_anywhere(); } + template + simdjson_inline simd8 shr() const { return __riscv_vsrl(value, N, VL8); } + template + simdjson_inline simd8 shl() const { return __riscv_vsll(value, N, VL8); } - // Saturated math - simdjson_inline simd8 saturating_add(const simd8 other) const { return __lasx_xvsadd_bu(*this, other); } - simdjson_inline simd8 saturating_sub(const simd8 other) const { return __lasx_xvssub_bu(*this, other); } - // Order-specific operations - simdjson_inline simd8 max_val(const simd8 other) const { return __lasx_xvmax_bu(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return __lasx_xvmin_bu(other, *this); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } - simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } - simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } - simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 lookup_table) const { + return __riscv_vrgather(lookup_table, value, VL8); + } - // Bit-specific operations - simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } - simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } - simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } - simdjson_inline simd8 any_bits_set(simd8 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 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 bits) const { return !bits_not_set_anywhere(bits); } - template - simdjson_inline simd8 shr() const { return simd8(__lasx_xvsrli_b(*this, N)); } - template - simdjson_inline simd8 shl() const { return simd8(__lasx_xvslli_b(*this, N)); } - }; + // compress inactive elements, to match AVX-512 behavior + template + 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 - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 2, "LASX kernel should use two registers per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed + // Signed bytes + template<> + struct simd8 { + vint8_t value; + static constexpr int SIZE = sizeof(value); - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1) : chunks{ chunk0, chunk1 } {} - simdjson_inline simd8x64(const T ptr[64]) : chunks{ simd8::load(ptr), simd8::load(ptr + 32) } {} + 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 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 operator const vint8_t&() const { return this->value; } + simdjson_inline operator vint8_t&() { return this->value; } - simdjson_inline void store(T ptr[64]) const { - this->chunks[0].store(ptr + sizeof(simd8) * 0); - this->chunks[1].store(ptr + sizeof(simd8) * 1); - } + 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 + }) {} - 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); - } + // Repeat 16 values as many times as necessary (usually for lookup tables) + simdjson_inline static simd8 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - simdjson_inline simd8 reduce_or() const { - return this->chunks[0] | this->chunks[1]; - } + 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 uint64_t eq(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] == mask, - this->chunks[1] == mask - ).to_bitmask(); - } - simdjson_inline uint64_t eq(const simd8x64& other) const { - return simd8x64( - this->chunks[0] == other.chunks[0], - this->chunks[1] == other.chunks[1] - ).to_bitmask(); - } + simdjson_inline void store(int8_t dst[VL8]) const { return __riscv_vse8(dst, value, VL8); } - simdjson_inline uint64_t lteq(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] <= mask, - this->chunks[1] <= mask - ).to_bitmask(); - } - }; // struct simd8x64 + // Explicit conversion to/from unsigned + simdjson_inline explicit simd8(const vuint8_t other): simd8(__riscv_vreinterpret_i8m1(other)) {} + simdjson_inline explicit operator simd8() const { return __riscv_vreinterpret_u8m1(value); } - } // namespace simd - } // unnamed namespace - } // namespace lasx + // Math + simdjson_inline simd8 operator+(const simd8 other) const { return __riscv_vadd(value, other, VL8); } + simdjson_inline simd8 operator-(const simd8 other) const { return __riscv_vsub(value, other, VL8); } + simdjson_inline simd8& operator+=(const simd8 other) { value = *this + other; return *this; } + simdjson_inline simd8& operator-=(const simd8 other) { value = *this - other; return *this; } + + // Order-sensitive comparisons + simdjson_inline simd8 max_val( const simd8 other) const { return __riscv_vmax( value, other, VL8); } + simdjson_inline simd8 min_val( const simd8 other) const { return __riscv_vmin( value, other, VL8); } + simdjson_inline simd8 operator>( const simd8 other) const { return __riscv_vmsgt(value, other, VL8); } + simdjson_inline simd8 operator<( const simd8 other) const { return __riscv_vmslt(value, other, VL8); } + simdjson_inline simd8 operator==(const simd8 other) const { return __riscv_vmseq(value, other, VL8); } + + template + simdjson_inline simd8 prev(const simd8 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 + simdjson_inline simd8 lookup_16(simd8 lookup_table) const { + return __riscv_vrgather(lookup_table, value, VL8); + } + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; + + template + struct simd8x64; + template<> + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + vuint8x64_t value; + +#if __riscv_v_fixed_vlen >= 512 + template simd8 get() const { return value; } +#else + template simd8 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& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& 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 + +} // namespace simd +} // unnamed namespace +} // namespace rvv_vls } // 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 static 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 v(src); - v.store(dst); - return { - static_cast((v == '\\').to_bitmask()), // bs_bits - static_cast((v == '"').to_bitmask()), // quote_bits - }; - } - - } // 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 generic/amalgamated.h for lasx: #include */ -/* begin file generic/amalgamated.h for lasx */ +#endif // SIMDJSON_RVV_VLS_SIMD_H +/* end file simdjson/rvv-vls/simd.h */ +/* including generic/amalgamated.h for rvv_vls: #include */ +/* begin file generic/amalgamated.h for rvv_vls */ #if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_SRC_GENERIC_DEPENDENCIES_H) #error generic/dependencies.h must be included before generic/amalgamated.h! #endif -/* including generic/base.h for lasx: #include */ -/* begin file generic/base.h for lasx */ +/* including generic/base.h for rvv_vls: #include */ +/* begin file generic/base.h for rvv_vls */ #ifndef SIMDJSON_SRC_GENERIC_BASE_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -49445,19 +58029,19 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace { +namespace rvv_vls { +namespace { - struct json_character_block; +struct json_character_block; - } // unnamed namespace - } // namespace lasx +} // unnamed namespace +} // namespace rvv_vls } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_BASE_H -/* end file generic/base.h for lasx */ -/* including generic/dom_parser_implementation.h for lasx: #include */ -/* begin file generic/dom_parser_implementation.h for lasx */ +/* end file generic/base.h for rvv_vls */ +/* including generic/dom_parser_implementation.h for rvv_vls: #include */ +/* begin file generic/dom_parser_implementation.h for rvv_vls */ #ifndef SIMDJSON_SRC_GENERIC_DOM_PARSER_IMPLEMENTATION_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -49468,20 +58052,20 @@ namespace simdjson { // Interface a dom parser implementation must fulfill namespace simdjson { - namespace lasx { - namespace { +namespace rvv_vls { +namespace { - simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3); - simdjson_inline bool is_ascii(const simd8x64& input); +simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3); +simdjson_inline bool is_ascii(const simd8x64& input); - } // unnamed namespace - } // namespace lasx +} // unnamed namespace +} // namespace rvv_vls } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_DOM_PARSER_IMPLEMENTATION_H -/* end file generic/dom_parser_implementation.h for lasx */ -/* including generic/json_character_block.h for lasx: #include */ -/* begin file generic/json_character_block.h for lasx */ +/* end file generic/dom_parser_implementation.h for rvv_vls */ +/* including generic/json_character_block.h for rvv_vls: #include */ +/* begin file generic/json_character_block.h for rvv_vls */ #ifndef SIMDJSON_SRC_GENERIC_JSON_CHARACTER_BLOCK_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -49490,32 +58074,32 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace { +namespace rvv_vls { +namespace { - struct json_character_block { - static simdjson_inline json_character_block classify(const simd::simd8x64& in); +struct json_character_block { + static simdjson_inline json_character_block classify(const simd::simd8x64& in); - simdjson_inline uint64_t whitespace() const noexcept { return _whitespace; } - simdjson_inline uint64_t op() const noexcept { return _op; } - simdjson_inline uint64_t scalar() const noexcept { return ~(op() | whitespace()); } + simdjson_inline uint64_t whitespace() const noexcept { return _whitespace; } + simdjson_inline uint64_t op() const noexcept { return _op; } + simdjson_inline uint64_t scalar() const noexcept { return ~(op() | whitespace()); } - uint64_t _whitespace; - uint64_t _op; - }; + uint64_t _whitespace; + uint64_t _op; +}; - } // unnamed namespace - } // namespace lasx +} // unnamed namespace +} // namespace rvv_vls } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_JSON_CHARACTER_BLOCK_H -/* end file generic/json_character_block.h for lasx */ -/* end file generic/amalgamated.h for lasx */ -/* including generic/stage1/amalgamated.h for lasx: #include */ -/* begin file generic/stage1/amalgamated.h for lasx */ +/* end file generic/json_character_block.h for rvv_vls */ +/* end file generic/amalgamated.h for rvv_vls */ +/* including generic/stage1/amalgamated.h for rvv_vls: #include */ +/* begin file generic/stage1/amalgamated.h for rvv_vls */ // Stuff other things depend on -/* including generic/stage1/base.h for lasx: #include */ -/* begin file generic/stage1/base.h for lasx */ +/* including generic/stage1/base.h for rvv_vls: #include */ +/* begin file generic/stage1/base.h for rvv_vls */ #ifndef SIMDJSON_SRC_GENERIC_STAGE1_BASE_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -49524,36 +58108,36 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace { - namespace stage1 { +namespace rvv_vls { +namespace { +namespace stage1 { - class bit_indexer; - template - struct buf_block_reader; - struct json_block; - class json_minifier; - class json_scanner; - struct json_string_block; - class json_string_scanner; - class json_structural_indexer; +class bit_indexer; +template +struct buf_block_reader; +struct json_block; +class json_minifier; +class json_scanner; +struct json_string_block; +class json_string_scanner; +class json_structural_indexer; - } // namespace stage1 +} // namespace stage1 - namespace utf8_validation { - struct utf8_checker; - } // namespace utf8_validation +namespace utf8_validation { +struct utf8_checker; +} // namespace utf8_validation - using utf8_validation::utf8_checker; +using utf8_validation::utf8_checker; - } // unnamed namespace - } // namespace lasx +} // unnamed namespace +} // namespace rvv_vls } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_BASE_H -/* end file generic/stage1/base.h for lasx */ -/* including generic/stage1/buf_block_reader.h for lasx: #include */ -/* begin file generic/stage1/buf_block_reader.h for lasx */ +/* end file generic/stage1/base.h for rvv_vls */ +/* including generic/stage1/buf_block_reader.h for rvv_vls: #include */ +/* begin file generic/stage1/buf_block_reader.h for rvv_vls */ #ifndef SIMDJSON_SRC_GENERIC_STAGE1_BUF_BLOCK_READER_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -49564,115 +58148,115 @@ namespace simdjson { #include namespace simdjson { - namespace lasx { - namespace { - namespace stage1 { +namespace rvv_vls { +namespace { +namespace stage1 { - // Walks through a buffer in block-sized increments, loading the last part with spaces - template - struct buf_block_reader { - public: - simdjson_inline buf_block_reader(const uint8_t* _buf, size_t _len); - simdjson_inline size_t block_index(); - simdjson_inline bool has_full_block() const; - simdjson_inline const uint8_t* full_block() const; - /** - * Get the last block, padded with spaces. - * - * There will always be a last block, with at least 1 byte, unless len == 0 (in which case this - * function fills the buffer with spaces and returns 0. In particular, if len == STEP_SIZE there - * will be 0 full_blocks and 1 remainder block with STEP_SIZE bytes and no spaces for padding. - * - * @return the number of effective characters in the last block. - */ - simdjson_inline size_t get_remainder(uint8_t* dst) const; - simdjson_inline void advance(); - private: - const uint8_t* buf; - const size_t len; - const size_t lenminusstep; - size_t idx; - }; +// Walks through a buffer in block-sized increments, loading the last part with spaces +template +struct buf_block_reader { +public: + simdjson_inline buf_block_reader(const uint8_t *_buf, size_t _len); + simdjson_inline size_t block_index(); + simdjson_inline bool has_full_block() const; + simdjson_inline const uint8_t *full_block() const; + /** + * Get the last block, padded with spaces. + * + * There will always be a last block, with at least 1 byte, unless len == 0 (in which case this + * function fills the buffer with spaces and returns 0. In particular, if len == STEP_SIZE there + * will be 0 full_blocks and 1 remainder block with STEP_SIZE bytes and no spaces for padding. + * + * @return the number of effective characters in the last block. + */ + simdjson_inline size_t get_remainder(uint8_t *dst) const; + simdjson_inline void advance(); +private: + const uint8_t *buf; + const size_t len; + const size_t lenminusstep; + size_t idx; +}; - // Routines to print masks and text for debugging bitmask operations - simdjson_unused static char* format_input_text_64(const uint8_t* text) { - static char buf[sizeof(simd8x64) + 1]; - for (size_t i = 0; i < sizeof(simd8x64); i++) { - buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]); - } - buf[sizeof(simd8x64)] = '\0'; - return buf; - } +// Routines to print masks and text for debugging bitmask operations +simdjson_unused static char * format_input_text_64(const uint8_t *text) { + static char buf[sizeof(simd8x64) + 1]; + for (size_t i=0; i); i++) { + buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]); + } + buf[sizeof(simd8x64)] = '\0'; + return buf; +} - // Routines to print masks and text for debugging bitmask operations - simdjson_unused static char* format_input_text(const simd8x64& in) { - static char buf[sizeof(simd8x64) + 1]; - in.store(reinterpret_cast(buf)); - for (size_t i = 0; i < sizeof(simd8x64); i++) { - if (buf[i] < ' ') { buf[i] = '_'; } - } - buf[sizeof(simd8x64)] = '\0'; - return buf; - } +// Routines to print masks and text for debugging bitmask operations +simdjson_unused static char * format_input_text(const simd8x64& in) { + static char buf[sizeof(simd8x64) + 1]; + in.store(reinterpret_cast(buf)); + for (size_t i=0; i); i++) { + if (buf[i] < ' ') { buf[i] = '_'; } + } + buf[sizeof(simd8x64)] = '\0'; + return buf; +} - simdjson_unused static char* format_input_text(const simd8x64& in, uint64_t mask) { - static char buf[sizeof(simd8x64) + 1]; - in.store(reinterpret_cast(buf)); - for (size_t i = 0; i < sizeof(simd8x64); i++) { - if (buf[i] <= ' ') { buf[i] = '_'; } - if (!(mask & (size_t(1) << i))) { buf[i] = ' '; } - } - buf[sizeof(simd8x64)] = '\0'; - return buf; - } +simdjson_unused static char * format_input_text(const simd8x64& in, uint64_t mask) { + static char buf[sizeof(simd8x64) + 1]; + in.store(reinterpret_cast(buf)); + for (size_t i=0; i); i++) { + if (buf[i] <= ' ') { buf[i] = '_'; } + if (!(mask & (size_t(1) << i))) { buf[i] = ' '; } + } + buf[sizeof(simd8x64)] = '\0'; + return buf; +} - simdjson_unused static char* format_mask(uint64_t mask) { - static char buf[sizeof(simd8x64) + 1]; - for (size_t i = 0; i < 64; i++) { - buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' '; - } - buf[64] = '\0'; - return buf; - } +simdjson_unused static char * format_mask(uint64_t mask) { + static char buf[sizeof(simd8x64) + 1]; + for (size_t i=0; i<64; i++) { + buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' '; + } + buf[64] = '\0'; + return buf; +} - template - simdjson_inline buf_block_reader::buf_block_reader(const uint8_t* _buf, size_t _len) : buf{ _buf }, len{ _len }, lenminusstep{ len < STEP_SIZE ? 0 : len - STEP_SIZE }, idx{ 0 } {} +template +simdjson_inline buf_block_reader::buf_block_reader(const uint8_t *_buf, size_t _len) : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE}, idx{0} {} - template - simdjson_inline size_t buf_block_reader::block_index() { return idx; } +template +simdjson_inline size_t buf_block_reader::block_index() { return idx; } - template - simdjson_inline bool buf_block_reader::has_full_block() const { - return idx < lenminusstep; - } +template +simdjson_inline bool buf_block_reader::has_full_block() const { + return idx < lenminusstep; +} - template - simdjson_inline const uint8_t* buf_block_reader::full_block() const { - return &buf[idx]; - } +template +simdjson_inline const uint8_t *buf_block_reader::full_block() const { + return &buf[idx]; +} - template - simdjson_inline size_t buf_block_reader::get_remainder(uint8_t* dst) const { - if (len == idx) { return 0; } // memcpy(dst, null, 0) will trigger an error with some sanitizers - std::memset(dst, 0x20, STEP_SIZE); // std::memset STEP_SIZE because it's more efficient to write out 8 or 16 bytes at once. - std::memcpy(dst, buf + idx, len - idx); - return len - idx; - } +template +simdjson_inline size_t buf_block_reader::get_remainder(uint8_t *dst) const { + if(len == idx) { return 0; } // memcpy(dst, null, 0) will trigger an error with some sanitizers + std::memset(dst, 0x20, STEP_SIZE); // std::memset STEP_SIZE because it's more efficient to write out 8 or 16 bytes at once. + std::memcpy(dst, buf + idx, len - idx); + return len - idx; +} - template - simdjson_inline void buf_block_reader::advance() { - idx += STEP_SIZE; - } +template +simdjson_inline void buf_block_reader::advance() { + idx += STEP_SIZE; +} - } // namespace stage1 - } // unnamed namespace - } // namespace lasx +} // namespace stage1 +} // unnamed namespace +} // namespace rvv_vls } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_BUF_BLOCK_READER_H -/* end file generic/stage1/buf_block_reader.h for lasx */ -/* including generic/stage1/json_escape_scanner.h for lasx: #include */ -/* begin file generic/stage1/json_escape_scanner.h for lasx */ +/* end file generic/stage1/buf_block_reader.h for rvv_vls */ +/* including generic/stage1/json_escape_scanner.h for rvv_vls: #include */ +/* begin file generic/stage1/json_escape_scanner.h for rvv_vls */ #ifndef SIMDJSON_SRC_GENERIC_STAGE1_JSON_ESCAPE_SCANNER_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -49682,151 +58266,151 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace { - namespace stage1 { +namespace rvv_vls { +namespace { +namespace stage1 { - /** - * Scans for escape characters in JSON, taking care with multiple backslashes (\\n vs. \n). - */ - struct json_escape_scanner { - /** The actual escape characters (the backslashes themselves). */ - uint64_t next_is_escaped = 0ULL; +/** + * Scans for escape characters in JSON, taking care with multiple backslashes (\\n vs. \n). + */ +struct json_escape_scanner { + /** The actual escape characters (the backslashes themselves). */ + uint64_t next_is_escaped = 0ULL; - struct escaped_and_escape { - /** - * Mask of escaped characters. - * - * ``` - * \n \\n \\\n \\\\n \ - * 0100100010100101000 - * n \ \ n \ \ - * ``` - */ - uint64_t escaped; - /** - * Mask of escape characters. - * - * ``` - * \n \\n \\\n \\\\n \ - * 1001000101001010001 - * \ \ \ \ \ \ \ - * ``` - */ - uint64_t escape; - }; + struct escaped_and_escape { + /** + * Mask of escaped characters. + * + * ``` + * \n \\n \\\n \\\\n \ + * 0100100010100101000 + * n \ \ n \ \ + * ``` + */ + uint64_t escaped; + /** + * Mask of escape characters. + * + * ``` + * \n \\n \\\n \\\\n \ + * 1001000101001010001 + * \ \ \ \ \ \ \ + * ``` + */ + uint64_t escape; + }; - /** - * Get a mask of both escape and escaped characters (the characters following a backslash). - * - * @param potential_escape A mask of the character that can escape others (but could be - * escaped itself). e.g. block.eq('\\') - */ - simdjson_really_inline escaped_and_escape next(uint64_t backslash) noexcept { + /** + * Get a mask of both escape and escaped characters (the characters following a backslash). + * + * @param potential_escape A mask of the character that can escape others (but could be + * escaped itself). e.g. block.eq('\\') + */ + simdjson_really_inline escaped_and_escape next(uint64_t backslash) noexcept { #if !SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT - if (!backslash) { return { next_escaped_without_backslashes(), 0 }; } + if (!backslash) { return {next_escaped_without_backslashes(), 0}; } #endif - // | | Mask (shows characters instead of 1's) | Depth | Instructions | - // |--------------------------------|----------------------------------------|-------|---------------------| - // | string | `\\n_\\\n___\\\n___\\\\___\\\\__\\\` | | | - // | | ` even odd even odd odd` | | | - // | potential_escape | ` \ \\\ \\\ \\\\ \\\\ \\\` | 1 | 1 (backslash & ~first_is_escaped) - // | escape_and_terminal_code | ` \n \ \n \ \n \ \ \ \ \ \` | 5 | 5 (next_escape_and_terminal_code()) - // | escaped | `\ \ n \ n \ \ \ \ \ ` X | 6 | 7 (escape_and_terminal_code ^ (potential_escape | first_is_escaped)) - // | escape | ` \ \ \ \ \ \ \ \ \ \` | 6 | 8 (escape_and_terminal_code & backslash) - // | first_is_escaped | `\ ` | 7 (*) | 9 (escape >> 63) () - // (*) this is not needed until the next iteration - uint64_t escape_and_terminal_code = next_escape_and_terminal_code(backslash & ~this->next_is_escaped); - uint64_t escaped = escape_and_terminal_code ^ (backslash | this->next_is_escaped); - uint64_t escape = escape_and_terminal_code & backslash; - this->next_is_escaped = escape >> 63; - return { escaped, escape }; - } + // | | Mask (shows characters instead of 1's) | Depth | Instructions | + // |--------------------------------|----------------------------------------|-------|---------------------| + // | string | `\\n_\\\n___\\\n___\\\\___\\\\__\\\` | | | + // | | ` even odd even odd odd` | | | + // | potential_escape | ` \ \\\ \\\ \\\\ \\\\ \\\` | 1 | 1 (backslash & ~first_is_escaped) + // | escape_and_terminal_code | ` \n \ \n \ \n \ \ \ \ \ \` | 5 | 5 (next_escape_and_terminal_code()) + // | escaped | `\ \ n \ n \ \ \ \ \ ` X | 6 | 7 (escape_and_terminal_code ^ (potential_escape | first_is_escaped)) + // | escape | ` \ \ \ \ \ \ \ \ \ \` | 6 | 8 (escape_and_terminal_code & backslash) + // | first_is_escaped | `\ ` | 7 (*) | 9 (escape >> 63) () + // (*) this is not needed until the next iteration + uint64_t escape_and_terminal_code = next_escape_and_terminal_code(backslash & ~this->next_is_escaped); + uint64_t escaped = escape_and_terminal_code ^ (backslash | this->next_is_escaped); + uint64_t escape = escape_and_terminal_code & backslash; + this->next_is_escaped = escape >> 63; + return {escaped, escape}; + } - private: - static constexpr const uint64_t ODD_BITS = 0xAAAAAAAAAAAAAAAAULL; +private: + static constexpr const uint64_t ODD_BITS = 0xAAAAAAAAAAAAAAAAULL; - simdjson_really_inline uint64_t next_escaped_without_backslashes() noexcept { - uint64_t escaped = this->next_is_escaped; - this->next_is_escaped = 0; - return escaped; - } + simdjson_really_inline uint64_t next_escaped_without_backslashes() noexcept { + uint64_t escaped = this->next_is_escaped; + this->next_is_escaped = 0; + return escaped; + } - /** - * Returns a mask of the next escape characters (masking out escaped backslashes), along with - * any non-backslash escape codes. - * - * \n \\n \\\n \\\\n returns: - * \n \ \ \n \ \ - * 11 100 1011 10100 - * - * You are expected to mask out the first bit yourself if the previous block had a trailing - * escape. - * - * & the result with potential_escape to get just the escape characters. - * ^ the result with (potential_escape | first_is_escaped) to get escaped characters. - */ - static simdjson_really_inline uint64_t next_escape_and_terminal_code(uint64_t potential_escape) noexcept { - // If we were to just shift and mask out any odd bits, we'd actually get a *half* right answer: - // any even-aligned backslash runs would be correct! Odd-aligned backslash runs would be - // inverted (\\\ would be 010 instead of 101). - // - // ``` - // string: | ____\\\\_\\\\_____ | - // maybe_escaped | ODD | \ \ \ \ | - // even-aligned ^^^ ^^^^ odd-aligned - // ``` - // - // Taking that into account, our basic strategy is: - // - // 1. Use subtraction to produce a mask with 1's for even-aligned runs and 0's for - // odd-aligned runs. - // 2. XOR all odd bits, which masks out the odd bits in even-aligned runs, and brings IN the - // odd bits in odd-aligned runs. - // 3. & with backslash to clean up any stray bits. - // runs are set to 0, and then XORing with "odd": - // - // | | Mask (shows characters instead of 1's) | Instructions | - // |--------------------------------|----------------------------------------|---------------------| - // | string | `\\n_\\\n___\\\n___\\\\___\\\\__\\\` | - // | | ` even odd even odd odd` | - // | maybe_escaped | ` n \\n \\n \\\_ \\\_ \\` X | 1 (potential_escape << 1) - // | maybe_escaped_and_odd | ` \n_ \\n _ \\\n_ _ \\\__ _\\\_ \\\` | 1 (maybe_escaped | odd) - // | even_series_codes_and_odd | ` n_\\\ _ n_ _\\\\ _ _ ` | 1 (maybe_escaped_and_odd - potential_escape) - // | escape_and_terminal_code | ` \n \ \n \ \n \ \ \ \ \ \` | 1 (^ odd) - // + /** + * Returns a mask of the next escape characters (masking out escaped backslashes), along with + * any non-backslash escape codes. + * + * \n \\n \\\n \\\\n returns: + * \n \ \ \n \ \ + * 11 100 1011 10100 + * + * You are expected to mask out the first bit yourself if the previous block had a trailing + * escape. + * + * & the result with potential_escape to get just the escape characters. + * ^ the result with (potential_escape | first_is_escaped) to get escaped characters. + */ + static simdjson_really_inline uint64_t next_escape_and_terminal_code(uint64_t potential_escape) noexcept { + // If we were to just shift and mask out any odd bits, we'd actually get a *half* right answer: + // any even-aligned backslash runs would be correct! Odd-aligned backslash runs would be + // inverted (\\\ would be 010 instead of 101). + // + // ``` + // string: | ____\\\\_\\\\_____ | + // maybe_escaped | ODD | \ \ \ \ | + // even-aligned ^^^ ^^^^ odd-aligned + // ``` + // + // Taking that into account, our basic strategy is: + // + // 1. Use subtraction to produce a mask with 1's for even-aligned runs and 0's for + // odd-aligned runs. + // 2. XOR all odd bits, which masks out the odd bits in even-aligned runs, and brings IN the + // odd bits in odd-aligned runs. + // 3. & with backslash to clean up any stray bits. + // runs are set to 0, and then XORing with "odd": + // + // | | Mask (shows characters instead of 1's) | Instructions | + // |--------------------------------|----------------------------------------|---------------------| + // | string | `\\n_\\\n___\\\n___\\\\___\\\\__\\\` | + // | | ` even odd even odd odd` | + // | maybe_escaped | ` n \\n \\n \\\_ \\\_ \\` X | 1 (potential_escape << 1) + // | maybe_escaped_and_odd | ` \n_ \\n _ \\\n_ _ \\\__ _\\\_ \\\` | 1 (maybe_escaped | odd) + // | even_series_codes_and_odd | ` n_\\\ _ n_ _\\\\ _ _ ` | 1 (maybe_escaped_and_odd - potential_escape) + // | escape_and_terminal_code | ` \n \ \n \ \n \ \ \ \ \ \` | 1 (^ odd) + // - // Escaped characters are characters following an escape. - uint64_t maybe_escaped = potential_escape << 1; + // Escaped characters are characters following an escape. + uint64_t maybe_escaped = potential_escape << 1; - // To distinguish odd from even escape sequences, therefore, we turn on any *starting* - // escapes that are on an odd byte. (We actually bring in all odd bits, for speed.) - // - Odd runs of backslashes are 0000, and the code at the end ("n" in \n or \\n) is 1. - // - Odd runs of backslashes are 1111, and the code at the end ("n" in \n or \\n) is 0. - // - All other odd bytes are 1, and even bytes are 0. - uint64_t maybe_escaped_and_odd_bits = maybe_escaped | ODD_BITS; - uint64_t even_series_codes_and_odd_bits = maybe_escaped_and_odd_bits - potential_escape; + // To distinguish odd from even escape sequences, therefore, we turn on any *starting* + // escapes that are on an odd byte. (We actually bring in all odd bits, for speed.) + // - Odd runs of backslashes are 0000, and the code at the end ("n" in \n or \\n) is 1. + // - Odd runs of backslashes are 1111, and the code at the end ("n" in \n or \\n) is 0. + // - All other odd bytes are 1, and even bytes are 0. + uint64_t maybe_escaped_and_odd_bits = maybe_escaped | ODD_BITS; + uint64_t even_series_codes_and_odd_bits = maybe_escaped_and_odd_bits - potential_escape; - // Now we flip all odd bytes back with xor. This: - // - Makes odd runs of backslashes go from 0000 to 1010 - // - Makes even runs of backslashes go from 1111 to 1010 - // - Sets actually-escaped codes to 1 (the n in \n and \\n: \n = 11, \\n = 100) - // - Resets all other bytes to 0 - return even_series_codes_and_odd_bits ^ ODD_BITS; - } - }; + // Now we flip all odd bytes back with xor. This: + // - Makes odd runs of backslashes go from 0000 to 1010 + // - Makes even runs of backslashes go from 1111 to 1010 + // - Sets actually-escaped codes to 1 (the n in \n and \\n: \n = 11, \\n = 100) + // - Resets all other bytes to 0 + return even_series_codes_and_odd_bits ^ ODD_BITS; + } +}; - } // namespace stage1 - } // unnamed namespace - } // namespace lasx +} // namespace stage1 +} // unnamed namespace +} // namespace rvv_vls } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRING_SCANNER_H -/* end file generic/stage1/json_escape_scanner.h for lasx */ -/* including generic/stage1/json_string_scanner.h for lasx: #include */ -/* begin file generic/stage1/json_string_scanner.h for lasx */ +/* end file generic/stage1/json_escape_scanner.h for rvv_vls */ +/* including generic/stage1/json_string_scanner.h for rvv_vls: #include */ +/* begin file generic/stage1/json_string_scanner.h for rvv_vls */ #ifndef SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRING_SCANNER_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -49836,99 +58420,99 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace { - namespace stage1 { +namespace rvv_vls { +namespace { +namespace stage1 { - struct json_string_block { - // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 - simdjson_really_inline json_string_block(uint64_t escaped, uint64_t quote, uint64_t in_string) : - _escaped(escaped), _quote(quote), _in_string(in_string) {} +struct json_string_block { + // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 + simdjson_really_inline json_string_block(uint64_t escaped, uint64_t quote, uint64_t in_string) : + _escaped(escaped), _quote(quote), _in_string(in_string) {} - // Escaped characters (characters following an escape() character) - simdjson_really_inline uint64_t escaped() const { return _escaped; } - // Real (non-backslashed) quotes - simdjson_really_inline uint64_t quote() const { return _quote; } - // Only characters inside the string (not including the quotes) - simdjson_really_inline uint64_t string_content() const { return _in_string & ~_quote; } - // Return a mask of whether the given characters are inside a string (only works on non-quotes) - simdjson_really_inline uint64_t non_quote_inside_string(uint64_t mask) const { return mask & _in_string; } - // Return a mask of whether the given characters are inside a string (only works on non-quotes) - simdjson_really_inline uint64_t non_quote_outside_string(uint64_t mask) const { return mask & ~_in_string; } - // Tail of string (everything except the start quote) - simdjson_really_inline uint64_t string_tail() const { return _in_string ^ _quote; } + // Escaped characters (characters following an escape() character) + simdjson_really_inline uint64_t escaped() const { return _escaped; } + // Real (non-backslashed) quotes + simdjson_really_inline uint64_t quote() const { return _quote; } + // Only characters inside the string (not including the quotes) + simdjson_really_inline uint64_t string_content() const { return _in_string & ~_quote; } + // Return a mask of whether the given characters are inside a string (only works on non-quotes) + simdjson_really_inline uint64_t non_quote_inside_string(uint64_t mask) const { return mask & _in_string; } + // Return a mask of whether the given characters are inside a string (only works on non-quotes) + simdjson_really_inline uint64_t non_quote_outside_string(uint64_t mask) const { return mask & ~_in_string; } + // Tail of string (everything except the start quote) + simdjson_really_inline uint64_t string_tail() const { return _in_string ^ _quote; } - // escaped characters (backslashed--does not include the hex characters after \u) - uint64_t _escaped; - // real quotes (non-escaped ones) - uint64_t _quote; - // string characters (includes start quote but not end quote) - uint64_t _in_string; - }; + // escaped characters (backslashed--does not include the hex characters after \u) + uint64_t _escaped; + // real quotes (non-escaped ones) + uint64_t _quote; + // string characters (includes start quote but not end quote) + uint64_t _in_string; +}; - // Scans blocks for string characters, storing the state necessary to do so - class json_string_scanner { - public: - simdjson_really_inline json_string_block next(const simd::simd8x64& in); - // Returns either UNCLOSED_STRING or SUCCESS - simdjson_really_inline error_code finish(); +// Scans blocks for string characters, storing the state necessary to do so +class json_string_scanner { +public: + simdjson_really_inline json_string_block next(const simd::simd8x64& in); + // Returns either UNCLOSED_STRING or SUCCESS + simdjson_really_inline error_code finish(); - private: - // Scans for escape characters - json_escape_scanner escape_scanner{}; - // Whether the last iteration was still inside a string (all 1's = true, all 0's = false). - uint64_t prev_in_string = 0ULL; - }; +private: + // Scans for escape characters + json_escape_scanner escape_scanner{}; + // Whether the last iteration was still inside a string (all 1's = true, all 0's = false). + uint64_t prev_in_string = 0ULL; +}; - // - // Return a mask of all string characters plus end quotes. - // - // prev_escaped is overflow saying whether the next character is escaped. - // prev_in_string is overflow saying whether we're still in a string. - // - // Backslash sequences outside of quotes will be detected in stage 2. - // - simdjson_really_inline json_string_block json_string_scanner::next(const simd::simd8x64& in) { - const uint64_t backslash = in.eq('\\'); - const uint64_t escaped = escape_scanner.next(backslash).escaped; - const uint64_t quote = in.eq('"') & ~escaped; +// +// Return a mask of all string characters plus end quotes. +// +// prev_escaped is overflow saying whether the next character is escaped. +// prev_in_string is overflow saying whether we're still in a string. +// +// Backslash sequences outside of quotes will be detected in stage 2. +// +simdjson_really_inline json_string_block json_string_scanner::next(const simd::simd8x64& in) { + const uint64_t backslash = in.eq('\\'); + const uint64_t escaped = escape_scanner.next(backslash).escaped; + const uint64_t quote = in.eq('"') & ~escaped; - // - // prefix_xor flips on bits inside the string (and flips off the end quote). - // - // Then we xor with prev_in_string: if we were in a string already, its effect is flipped - // (characters inside strings are outside, and characters outside strings are inside). - // - const uint64_t in_string = prefix_xor(quote) ^ prev_in_string; + // + // prefix_xor flips on bits inside the string (and flips off the end quote). + // + // Then we xor with prev_in_string: if we were in a string already, its effect is flipped + // (characters inside strings are outside, and characters outside strings are inside). + // + const uint64_t in_string = prefix_xor(quote) ^ prev_in_string; - // - // Check if we're still in a string at the end of the box so the next block will know - // - prev_in_string = uint64_t(static_cast(in_string) >> 63); + // + // Check if we're still in a string at the end of the box so the next block will know + // + prev_in_string = uint64_t(static_cast(in_string) >> 63); - // Use ^ to turn the beginning quote off, and the end quote on. + // Use ^ to turn the beginning quote off, and the end quote on. - // We are returning a function-local object so either we get a move constructor - // or we get copy elision. - return json_string_block(escaped, quote, in_string); - } + // We are returning a function-local object so either we get a move constructor + // or we get copy elision. + return json_string_block(escaped, quote, in_string); +} - simdjson_really_inline error_code json_string_scanner::finish() { - if (prev_in_string) { - return UNCLOSED_STRING; - } - return SUCCESS; - } +simdjson_really_inline error_code json_string_scanner::finish() { + if (prev_in_string) { + return UNCLOSED_STRING; + } + return SUCCESS; +} - } // namespace stage1 - } // unnamed namespace - } // namespace lasx +} // namespace stage1 +} // unnamed namespace +} // namespace rvv_vls } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRING_SCANNER_H -/* end file generic/stage1/json_string_scanner.h for lasx */ -/* including generic/stage1/utf8_lookup4_algorithm.h for lasx: #include */ -/* begin file generic/stage1/utf8_lookup4_algorithm.h for lasx */ +/* end file generic/stage1/json_string_scanner.h for rvv_vls */ +/* including generic/stage1/utf8_lookup4_algorithm.h for rvv_vls: #include */ +/* begin file generic/stage1/utf8_lookup4_algorithm.h for rvv_vls */ #ifndef SIMDJSON_SRC_GENERIC_STAGE1_UTF8_LOOKUP4_ALGORITHM_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -49938,212 +58522,209 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace { - namespace utf8_validation { +namespace rvv_vls { +namespace { +namespace utf8_validation { - using namespace simd; +using namespace simd; - simdjson_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { - // Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) - // Bit 1 = Too Long (ASCII followed by continuation) - // Bit 2 = Overlong 3-byte - // Bit 4 = Surrogate - // Bit 5 = Overlong 2-byte - // Bit 7 = Two Continuations - constexpr const uint8_t TOO_SHORT = 1 << 0; // 11______ 0_______ - // 11______ 11______ - constexpr const uint8_t TOO_LONG = 1 << 1; // 0_______ 10______ - constexpr const uint8_t OVERLONG_3 = 1 << 2; // 11100000 100_____ - constexpr const uint8_t SURROGATE = 1 << 4; // 11101101 101_____ - constexpr const uint8_t OVERLONG_2 = 1 << 5; // 1100000_ 10______ - constexpr const uint8_t TWO_CONTS = 1 << 7; // 10______ 10______ - constexpr const uint8_t TOO_LARGE = 1 << 3; // 11110100 1001____ - // 11110100 101_____ - // 11110101 1001____ - // 11110101 101_____ - // 1111011_ 1001____ - // 1111011_ 101_____ - // 11111___ 1001____ - // 11111___ 101_____ - constexpr const uint8_t TOO_LARGE_1000 = 1 << 6; - // 11110101 1000____ - // 1111011_ 1000____ - // 11111___ 1000____ - constexpr const uint8_t OVERLONG_4 = 1 << 6; // 11110000 1000____ + simdjson_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { +// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) +// Bit 1 = Too Long (ASCII followed by continuation) +// Bit 2 = Overlong 3-byte +// Bit 4 = Surrogate +// Bit 5 = Overlong 2-byte +// Bit 7 = Two Continuations + constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______ + // 11______ 11______ + constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______ + constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____ + constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____ + constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______ + constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______ + constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____ + // 11110100 101_____ + // 11110101 1001____ + // 11110101 101_____ + // 1111011_ 1001____ + // 1111011_ 101_____ + // 11111___ 1001____ + // 11111___ 101_____ + constexpr const uint8_t TOO_LARGE_1000 = 1<<6; + // 11110101 1000____ + // 1111011_ 1000____ + // 11111___ 1000____ + constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____ - const simd8 byte_1_high = prev1.shr<4>().lookup_16( - // 0_______ ________ - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - // 10______ ________ - TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, - // 1100____ ________ - TOO_SHORT | OVERLONG_2, - // 1101____ ________ - TOO_SHORT, - // 1110____ ________ - TOO_SHORT | OVERLONG_3 | SURROGATE, - // 1111____ ________ - TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 - ); - constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . - const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( - // ____0000 ________ - CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, - // ____0001 ________ - CARRY | OVERLONG_2, - // ____001_ ________ - CARRY, - CARRY, + const simd8 byte_1_high = prev1.shr<4>().lookup_16( + // 0_______ ________ + TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, + TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, + // 10______ ________ + TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, + // 1100____ ________ + TOO_SHORT | OVERLONG_2, + // 1101____ ________ + TOO_SHORT, + // 1110____ ________ + TOO_SHORT | OVERLONG_3 | SURROGATE, + // 1111____ ________ + TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 + ); + constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . + const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( + // ____0000 ________ + CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, + // ____0001 ________ + CARRY | OVERLONG_2, + // ____001_ ________ + CARRY, + CARRY, - // ____0100 ________ - CARRY | TOO_LARGE, - // ____0101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____011_ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, + // ____0100 ________ + CARRY | TOO_LARGE, + // ____0101 ________ + CARRY | TOO_LARGE | TOO_LARGE_1000, + // ____011_ ________ + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____1___ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____1101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000 - ); - const simd8 byte_2_high = input.shr<4>().lookup_16( - // ________ 0_______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, + // ____1___ ________ + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000, + // ____1101 ________ + CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, + CARRY | TOO_LARGE | TOO_LARGE_1000, + CARRY | TOO_LARGE | TOO_LARGE_1000 + ); + const simd8 byte_2_high = input.shr<4>().lookup_16( + // ________ 0_______ + TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, + TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - // ________ 1000____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, - // ________ 1001____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, - // ________ 101_____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, + // ________ 1000____ + TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, + // ________ 1001____ + TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, + // ________ 101_____ + TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, + TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - // ________ 11______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT - ); - return (byte_1_high & byte_1_low & byte_2_high); - } - simdjson_inline simd8 check_multibyte_lengths(const simd8 input, - const simd8 prev_input, const simd8 sc) { - simd8 prev2 = input.prev<2>(prev_input); - simd8 prev3 = input.prev<3>(prev_input); - simd8 must23 = must_be_2_3_continuation(prev2, prev3); - simd8 must23_80 = must23 & uint8_t(0x80); - return must23_80 ^ sc; - } + // ________ 11______ + TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT + ); + return (byte_1_high & byte_1_low & byte_2_high); + } + simdjson_inline simd8 check_multibyte_lengths(const simd8 input, + const simd8 prev_input, const simd8 sc) { + simd8 prev2 = input.prev<2>(prev_input); + simd8 prev3 = input.prev<3>(prev_input); + simd8 must23 = must_be_2_3_continuation(prev2, prev3); + simd8 must23_80 = must23 & uint8_t(0x80); + return must23_80 ^ sc; + } - // - // Return nonzero if there are incomplete multibyte characters at the end of the block: - // e.g. if there is a 4-byte character, but it's 3 bytes from the end. - // - simdjson_inline simd8 is_incomplete(const simd8 input) { - // If the previous input's last 3 bytes match this, they're too short (they ended at EOF): - // ... 1111____ 111_____ 11______ -#if SIMDJSON_IMPLEMENTATION_ICELAKE - static const uint8_t max_array[64] = { - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0xf0u - 1, 0xe0u - 1, 0xc0u - 1 - }; + // + // Return nonzero if there are incomplete multibyte characters at the end of the block: + // e.g. if there is a 4-byte character, but it's 3 bytes from the end. + // + simdjson_inline simd8 is_incomplete(const simd8 input) { + // If the previous input's last 3 bytes match this, they're too short (they ended at EOF): + // ... 1111____ 111_____ 11______ +#if SIMDJSON_IMPLEMENTATION_ICELAKE || (SIMDJSON_IMPLEMENTATION_RVV_VLS && __riscv_v_fixed_vlen >= 512) + static const uint8_t max_array[64] = { + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0xf0u-1, 0xe0u-1, 0xc0u-1 + }; #else - static const uint8_t max_array[32] = { - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0xf0u - 1, 0xe0u - 1, 0xc0u - 1 - }; + static const uint8_t max_array[32] = { + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0xf0u-1, 0xe0u-1, 0xc0u-1 + }; #endif - const simd8 max_value(&max_array[sizeof(max_array) - sizeof(simd8)]); - return input.gt_bits(max_value); - } + const simd8 max_value(&max_array[sizeof(max_array)-sizeof(simd8)]); + return input.gt_bits(max_value); + } - struct utf8_checker { - // If this is nonzero, there has been a UTF-8 error. - simd8 error; - // The last input we received - simd8 prev_input_block; - // Whether the last input we received was incomplete (used for ASCII fast path) - simd8 prev_incomplete; + struct utf8_checker { + // If this is nonzero, there has been a UTF-8 error. + simd8 error; + // The last input we received + simd8 prev_input_block; + // Whether the last input we received was incomplete (used for ASCII fast path) + simd8 prev_incomplete; - // - // Check whether the current bytes are valid UTF-8. - // - simdjson_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { - // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes - // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) - simd8 prev1 = input.prev<1>(prev_input); - simd8 sc = check_special_cases(input, prev1); - this->error |= check_multibyte_lengths(input, prev_input, sc); - } + // + // Check whether the current bytes are valid UTF-8. + // + simdjson_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { + // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes + // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) + simd8 prev1 = input.prev<1>(prev_input); + simd8 sc = check_special_cases(input, prev1); + this->error |= check_multibyte_lengths(input, prev_input, sc); + } - // The only problem that can happen at EOF is that a multibyte character is too short - // or a byte value too large in the last bytes: check_special_cases only checks for bytes - // too large in the first of two bytes. - simdjson_inline void check_eof() { - // If the previous block had incomplete UTF-8 characters at the end, an ASCII block can't - // possibly finish them. - this->error |= this->prev_incomplete; - } + // The only problem that can happen at EOF is that a multibyte character is too short + // or a byte value too large in the last bytes: check_special_cases only checks for bytes + // too large in the first of two bytes. + simdjson_inline void check_eof() { + // If the previous block had incomplete UTF-8 characters at the end, an ASCII block can't + // possibly finish them. + this->error |= this->prev_incomplete; + } - simdjson_inline void check_next_input(const simd8x64& input) { - if (simdjson_likely(is_ascii(input))) { - this->error |= this->prev_incomplete; - } - else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 1) - || (simd8x64::NUM_CHUNKS == 2) - || (simd8x64::NUM_CHUNKS == 4), - "We support one, two or four chunks per 64-byte block."); - SIMDJSON_IF_CONSTEXPR(simd8x64::NUM_CHUNKS == 1) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - } -else SIMDJSON_IF_CONSTEXPR(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); + simdjson_inline void check_next_input(const simd8x64& input) { + if(simdjson_likely(is_ascii(input))) { + this->error |= this->prev_incomplete; + } else { + // you might think that a for-loop would work, but under Visual Studio, it is not good enough. + static_assert((simd8x64::NUM_CHUNKS == 1) + ||(simd8x64::NUM_CHUNKS == 2) + || (simd8x64::NUM_CHUNKS == 4), + "We support one, two or four chunks per 64-byte block."); + SIMDJSON_IF_CONSTEXPR (simd8x64::NUM_CHUNKS == 1) { + this->check_utf8_bytes(input.get<0>(), this->prev_input_block); + } else SIMDJSON_IF_CONSTEXPR (simd8x64::NUM_CHUNKS == 2) { + this->check_utf8_bytes(input.get<0>(), this->prev_input_block); + this->check_utf8_bytes(input.get<1>(), input.get<0>()); + } else SIMDJSON_IF_CONSTEXPR (simd8x64::NUM_CHUNKS == 4) { + this->check_utf8_bytes(input.get<0>(), this->prev_input_block); + this->check_utf8_bytes(input.get<1>(), input.get<0>()); + this->check_utf8_bytes(input.get<2>(), input.get<1>()); + this->check_utf8_bytes(input.get<3>(), input.get<2>()); } - else SIMDJSON_IF_CONSTEXPR(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - this->prev_incomplete = is_incomplete(input.chunks[simd8x64::NUM_CHUNKS - 1]); - this->prev_input_block = input.chunks[simd8x64::NUM_CHUNKS - 1]; - } - } - // do not forget to call check_eof! - simdjson_inline error_code errors() { - return this->error.any_bits_set_anywhere() ? error_code::UTF8_ERROR : error_code::SUCCESS; - } + this->prev_incomplete = is_incomplete(input.get::NUM_CHUNKS-1>()); + this->prev_input_block = input.get::NUM_CHUNKS-1>(); + } + } + // do not forget to call check_eof! + simdjson_warn_unused simdjson_inline error_code errors() { + return this->error.any_bits_set_anywhere() ? error_code::UTF8_ERROR : error_code::SUCCESS; + } - }; // struct utf8_checker - } // namespace utf8_validation + }; // struct utf8_checker +} // namespace utf8_validation - } // unnamed namespace - } // namespace lasx +} // unnamed namespace +} // namespace rvv_vls } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_UTF8_LOOKUP4_ALGORITHM_H -/* end file generic/stage1/utf8_lookup4_algorithm.h for lasx */ -/* including generic/stage1/json_scanner.h for lasx: #include */ -/* begin file generic/stage1/json_scanner.h for lasx */ +/* end file generic/stage1/utf8_lookup4_algorithm.h for rvv_vls */ +/* including generic/stage1/json_scanner.h for rvv_vls: #include */ +/* begin file generic/stage1/json_scanner.h for rvv_vls */ #ifndef SIMDJSON_SRC_GENERIC_STAGE1_JSON_SCANNER_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -50154,169 +58735,169 @@ else SIMDJSON_IF_CONSTEXPR(simd8x64::NUM_CHUNKS == 2) { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace { - namespace stage1 { +namespace rvv_vls { +namespace { +namespace stage1 { - /** - * A block of scanned json, with information on operators and scalars. - * - * We seek to identify pseudo-structural characters. Anything that is inside - * a string must be omitted (hence & ~_string.string_tail()). - * Otherwise, pseudo-structural characters come in two forms. - * 1. We have the structural characters ([,],{,},:, comma). The - * term 'structural character' is from the JSON RFC. - * 2. We have the 'scalar pseudo-structural characters'. - * Scalars are quotes, and any character except structural characters and white space. - * - * To identify the scalar pseudo-structural characters, we must look at what comes - * before them: it must be a space, a quote or a structural characters. - * Starting with simdjson v0.3, we identify them by - * negation: we identify everything that is followed by a non-quote scalar, - * and we negate that. Whatever remains must be a 'scalar pseudo-structural character'. - */ - struct json_block { - public: - // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 - simdjson_inline json_block(json_string_block&& string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : - _string(std::move(string)), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} - simdjson_inline json_block(json_string_block string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : - _string(string), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} +/** + * A block of scanned json, with information on operators and scalars. + * + * We seek to identify pseudo-structural characters. Anything that is inside + * a string must be omitted (hence & ~_string.string_tail()). + * Otherwise, pseudo-structural characters come in two forms. + * 1. We have the structural characters ([,],{,},:, comma). The + * term 'structural character' is from the JSON RFC. + * 2. We have the 'scalar pseudo-structural characters'. + * Scalars are quotes, and any character except structural characters and white space. + * + * To identify the scalar pseudo-structural characters, we must look at what comes + * before them: it must be a space, a quote or a structural characters. + * Starting with simdjson v0.3, we identify them by + * negation: we identify everything that is followed by a non-quote scalar, + * and we negate that. Whatever remains must be a 'scalar pseudo-structural character'. + */ +struct json_block { +public: + // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 + simdjson_inline json_block(json_string_block&& string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : + _string(std::move(string)), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} + simdjson_inline json_block(json_string_block string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : + _string(string), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} - /** - * The start of structurals. - * In simdjson prior to v0.3, these were called the pseudo-structural characters. - **/ - simdjson_inline uint64_t structural_start() const noexcept { return potential_structural_start() & ~_string.string_tail(); } - /** All JSON whitespace (i.e. not in a string) */ - simdjson_inline uint64_t whitespace() const noexcept { return non_quote_outside_string(_characters.whitespace()); } + /** + * The start of structurals. + * In simdjson prior to v0.3, these were called the pseudo-structural characters. + **/ + simdjson_inline uint64_t structural_start() const noexcept { return potential_structural_start() & ~_string.string_tail(); } + /** All JSON whitespace (i.e. not in a string) */ + simdjson_inline uint64_t whitespace() const noexcept { return non_quote_outside_string(_characters.whitespace()); } - // Helpers + // Helpers - /** Whether the given characters are inside a string (only works on non-quotes) */ - simdjson_inline uint64_t non_quote_inside_string(uint64_t mask) const noexcept { return _string.non_quote_inside_string(mask); } - /** Whether the given characters are outside a string (only works on non-quotes) */ - simdjson_inline uint64_t non_quote_outside_string(uint64_t mask) const noexcept { return _string.non_quote_outside_string(mask); } + /** Whether the given characters are inside a string (only works on non-quotes) */ + simdjson_inline uint64_t non_quote_inside_string(uint64_t mask) const noexcept { return _string.non_quote_inside_string(mask); } + /** Whether the given characters are outside a string (only works on non-quotes) */ + simdjson_inline uint64_t non_quote_outside_string(uint64_t mask) const noexcept { return _string.non_quote_outside_string(mask); } - // string and escape characters - json_string_block _string; - // whitespace, structural characters ('operators'), scalars - json_character_block _characters; - // whether the previous character was a scalar - uint64_t _follows_potential_nonquote_scalar; - private: - // Potential structurals (i.e. disregarding strings) + // string and escape characters + json_string_block _string; + // whitespace, structural characters ('operators'), scalars + json_character_block _characters; + // whether the previous character was a scalar + uint64_t _follows_potential_nonquote_scalar; +private: + // Potential structurals (i.e. disregarding strings) - /** - * structural elements ([,],{,},:, comma) plus scalar starts like 123, true and "abc". - * They may reside inside a string. - **/ - simdjson_inline uint64_t potential_structural_start() const noexcept { return _characters.op() | potential_scalar_start(); } - /** - * The start of non-operator runs, like 123, true and "abc". - * It main reside inside a string. - **/ - simdjson_inline uint64_t potential_scalar_start() const noexcept { - // The term "scalar" refers to anything except structural characters and white space - // (so letters, numbers, quotes). - // Whenever it is preceded by something that is not a structural element ({,},[,],:, ") nor a white-space - // then we know that it is irrelevant structurally. - return _characters.scalar() & ~follows_potential_scalar(); - } - /** - * Whether the given character is immediately after a non-operator like 123, true. - * The characters following a quote are not included. - */ - simdjson_inline uint64_t follows_potential_scalar() const noexcept { - // _follows_potential_nonquote_scalar: is defined as marking any character that follows a character - // that is not a structural element ({,},[,],:, comma) nor a quote (") and that is not a - // white space. - // It is understood that within quoted region, anything at all could be marked (irrelevant). - return _follows_potential_nonquote_scalar; - } - }; + /** + * structural elements ([,],{,},:, comma) plus scalar starts like 123, true and "abc". + * They may reside inside a string. + **/ + simdjson_inline uint64_t potential_structural_start() const noexcept { return _characters.op() | potential_scalar_start(); } + /** + * The start of non-operator runs, like 123, true and "abc". + * It main reside inside a string. + **/ + simdjson_inline uint64_t potential_scalar_start() const noexcept { + // The term "scalar" refers to anything except structural characters and white space + // (so letters, numbers, quotes). + // Whenever it is preceded by something that is not a structural element ({,},[,],:, ") nor a white-space + // then we know that it is irrelevant structurally. + return _characters.scalar() & ~follows_potential_scalar(); + } + /** + * Whether the given character is immediately after a non-operator like 123, true. + * The characters following a quote are not included. + */ + simdjson_inline uint64_t follows_potential_scalar() const noexcept { + // _follows_potential_nonquote_scalar: is defined as marking any character that follows a character + // that is not a structural element ({,},[,],:, comma) nor a quote (") and that is not a + // white space. + // It is understood that within quoted region, anything at all could be marked (irrelevant). + return _follows_potential_nonquote_scalar; + } +}; - /** - * Scans JSON for important bits: structural characters or 'operators', strings, and scalars. - * - * The scanner starts by calculating two distinct things: - * - string characters (taking \" into account) - * - structural characters or 'operators' ([]{},:, comma) - * and scalars (runs of non-operators like 123, true and "abc") - * - * To minimize data dependency (a key component of the scanner's speed), it finds these in parallel: - * in particular, the operator/scalar bit will find plenty of things that are actually part of - * strings. When we're done, json_block will fuse the two together by masking out tokens that are - * part of a string. - */ - class json_scanner { - public: - json_scanner() = default; - simdjson_inline json_block next(const simd::simd8x64& in); - // Returns either UNCLOSED_STRING or SUCCESS - simdjson_inline error_code finish(); +/** + * Scans JSON for important bits: structural characters or 'operators', strings, and scalars. + * + * The scanner starts by calculating two distinct things: + * - string characters (taking \" into account) + * - structural characters or 'operators' ([]{},:, comma) + * and scalars (runs of non-operators like 123, true and "abc") + * + * To minimize data dependency (a key component of the scanner's speed), it finds these in parallel: + * in particular, the operator/scalar bit will find plenty of things that are actually part of + * strings. When we're done, json_block will fuse the two together by masking out tokens that are + * part of a string. + */ +class json_scanner { +public: + json_scanner() = default; + simdjson_inline json_block next(const simd::simd8x64& in); + // Returns either UNCLOSED_STRING or SUCCESS + simdjson_warn_unused simdjson_inline error_code finish(); - private: - // Whether the last character of the previous iteration is part of a scalar token - // (anything except whitespace or a structural character/'operator'). - uint64_t prev_scalar = 0ULL; - json_string_scanner string_scanner{}; - }; +private: + // Whether the last character of the previous iteration is part of a scalar token + // (anything except whitespace or a structural character/'operator'). + uint64_t prev_scalar = 0ULL; + json_string_scanner string_scanner{}; +}; - // - // Check if the current character immediately follows a matching character. - // - // For example, this checks for quotes with backslashes in front of them: - // - // const uint64_t backslashed_quote = in.eq('"') & immediately_follows(in.eq('\'), prev_backslash); - // - simdjson_inline uint64_t follows(const uint64_t match, uint64_t& overflow) { - const uint64_t result = match << 1 | overflow; - overflow = match >> 63; - return result; - } +// +// Check if the current character immediately follows a matching character. +// +// For example, this checks for quotes with backslashes in front of them: +// +// const uint64_t backslashed_quote = in.eq('"') & immediately_follows(in.eq('\'), prev_backslash); +// +simdjson_inline uint64_t follows(const uint64_t match, uint64_t &overflow) { + const uint64_t result = match << 1 | overflow; + overflow = match >> 63; + return result; +} - simdjson_inline json_block json_scanner::next(const simd::simd8x64& in) { - json_string_block strings = string_scanner.next(in); - // identifies the white-space and the structural characters - json_character_block characters = json_character_block::classify(in); - // The term "scalar" refers to anything except structural characters and white space - // (so letters, numbers, quotes). - // We want follows_scalar to mark anything that follows a non-quote scalar (so letters and numbers). - // - // A terminal quote should either be followed by a structural character (comma, brace, bracket, colon) - // or nothing. However, we still want ' "a string"true ' to mark the 't' of 'true' as a potential - // pseudo-structural character just like we would if we had ' "a string" true '; otherwise we - // may need to add an extra check when parsing strings. - // - // Performance: there are many ways to skin this cat. - const uint64_t nonquote_scalar = characters.scalar() & ~strings.quote(); - uint64_t follows_nonquote_scalar = follows(nonquote_scalar, prev_scalar); - // We are returning a function-local object so either we get a move constructor - // or we get copy elision. - return json_block( - strings,// strings is a function-local object so either it moves or the copy is elided. - characters, - follows_nonquote_scalar - ); - } +simdjson_inline json_block json_scanner::next(const simd::simd8x64& in) { + json_string_block strings = string_scanner.next(in); + // identifies the white-space and the structural characters + json_character_block characters = json_character_block::classify(in); + // The term "scalar" refers to anything except structural characters and white space + // (so letters, numbers, quotes). + // We want follows_scalar to mark anything that follows a non-quote scalar (so letters and numbers). + // + // A terminal quote should either be followed by a structural character (comma, brace, bracket, colon) + // or nothing. However, we still want ' "a string"true ' to mark the 't' of 'true' as a potential + // pseudo-structural character just like we would if we had ' "a string" true '; otherwise we + // may need to add an extra check when parsing strings. + // + // Performance: there are many ways to skin this cat. + const uint64_t nonquote_scalar = characters.scalar() & ~strings.quote(); + uint64_t follows_nonquote_scalar = follows(nonquote_scalar, prev_scalar); + // We are returning a function-local object so either we get a move constructor + // or we get copy elision. + return json_block( + strings,// strings is a function-local object so either it moves or the copy is elided. + characters, + follows_nonquote_scalar + ); +} - simdjson_inline error_code json_scanner::finish() { - return string_scanner.finish(); - } +simdjson_warn_unused simdjson_inline error_code json_scanner::finish() { + return string_scanner.finish(); +} - } // namespace stage1 - } // unnamed namespace - } // namespace lasx +} // namespace stage1 +} // unnamed namespace +} // namespace rvv_vls } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_SCANNER_H -/* end file generic/stage1/json_scanner.h for lasx */ +/* end file generic/stage1/json_scanner.h for rvv_vls */ // All other declarations -/* including generic/stage1/find_next_document_index.h for lasx: #include */ -/* begin file generic/stage1/find_next_document_index.h for lasx */ +/* including generic/stage1/find_next_document_index.h for rvv_vls: #include */ +/* begin file generic/stage1/find_next_document_index.h for rvv_vls */ #ifndef SIMDJSON_SRC_GENERIC_STAGE1_FIND_NEXT_DOCUMENT_INDEX_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -50326,105 +58907,105 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace { - namespace stage1 { +namespace rvv_vls { +namespace { +namespace stage1 { - /** - * This algorithm is used to quickly identify the last structural position that - * makes up a complete document. - * - * It does this by going backwards and finding the last *document boundary* (a - * place where one value follows another without a comma between them). If the - * last document (the characters after the boundary) has an equal number of - * start and end brackets, it is considered complete. - * - * Simply put, we iterate over the structural characters, starting from - * the end. We consider that we found the end of a JSON document when the - * first element of the pair is NOT one of these characters: '{' '[' ':' ',' - * and when the second element is NOT one of these characters: '}' ']' ':' ','. - * - * This simple comparison works most of the time, but it does not cover cases - * where the batch's structural indexes contain a perfect amount of documents. - * In such a case, we do not have access to the structural index which follows - * the last document, therefore, we do not have access to the second element in - * the pair, and that means we cannot identify the last document. To fix this - * issue, we keep a count of the open and closed curly/square braces we found - * while searching for the pair. When we find a pair AND the count of open and - * closed curly/square braces is the same, we know that we just passed a - * complete document, therefore the last json buffer location is the end of the - * batch. - */ - simdjson_inline uint32_t find_next_document_index(dom_parser_implementation& parser) { - // Variant: do not count separately, just figure out depth - if (parser.n_structural_indexes == 0) { return 0; } - auto arr_cnt = 0; - auto obj_cnt = 0; - for (auto i = parser.n_structural_indexes - 1; i > 0; i--) { - auto idxb = parser.structural_indexes[i]; - switch (parser.buf[idxb]) { - case ':': - case ',': - continue; - case '}': - obj_cnt--; - continue; - case ']': - arr_cnt--; - continue; - case '{': - obj_cnt++; - break; - case '[': - arr_cnt++; - break; - } - auto idxa = parser.structural_indexes[i - 1]; - switch (parser.buf[idxa]) { - case '{': - case '[': - case ':': - case ',': - continue; - } - // Last document is complete, so the next document will appear after! - if (!arr_cnt && !obj_cnt) { - return parser.n_structural_indexes; - } - // Last document is incomplete; mark the document at i + 1 as the next one - return i; - } - // If we made it to the end, we want to finish counting to see if we have a full document. - switch (parser.buf[parser.structural_indexes[0]]) { - case '}': - obj_cnt--; - break; - case ']': - arr_cnt--; - break; - case '{': - obj_cnt++; - break; - case '[': - arr_cnt++; - break; - } - if (!arr_cnt && !obj_cnt) { - // We have a complete document. - return parser.n_structural_indexes; - } - return 0; - } +/** + * This algorithm is used to quickly identify the last structural position that + * makes up a complete document. + * + * It does this by going backwards and finding the last *document boundary* (a + * place where one value follows another without a comma between them). If the + * last document (the characters after the boundary) has an equal number of + * start and end brackets, it is considered complete. + * + * Simply put, we iterate over the structural characters, starting from + * the end. We consider that we found the end of a JSON document when the + * first element of the pair is NOT one of these characters: '{' '[' ':' ',' + * and when the second element is NOT one of these characters: '}' ']' ':' ','. + * + * This simple comparison works most of the time, but it does not cover cases + * where the batch's structural indexes contain a perfect amount of documents. + * In such a case, we do not have access to the structural index which follows + * the last document, therefore, we do not have access to the second element in + * the pair, and that means we cannot identify the last document. To fix this + * issue, we keep a count of the open and closed curly/square braces we found + * while searching for the pair. When we find a pair AND the count of open and + * closed curly/square braces is the same, we know that we just passed a + * complete document, therefore the last json buffer location is the end of the + * batch. + */ +simdjson_inline uint32_t find_next_document_index(dom_parser_implementation &parser) { + // Variant: do not count separately, just figure out depth + if(parser.n_structural_indexes == 0) { return 0; } + auto arr_cnt = 0; + auto obj_cnt = 0; + for (auto i = parser.n_structural_indexes - 1; i > 0; i--) { + auto idxb = parser.structural_indexes[i]; + switch (parser.buf[idxb]) { + case ':': + case ',': + continue; + case '}': + obj_cnt--; + continue; + case ']': + arr_cnt--; + continue; + case '{': + obj_cnt++; + break; + case '[': + arr_cnt++; + break; + } + auto idxa = parser.structural_indexes[i - 1]; + switch (parser.buf[idxa]) { + case '{': + case '[': + case ':': + case ',': + continue; + } + // Last document is complete, so the next document will appear after! + if (!arr_cnt && !obj_cnt) { + return parser.n_structural_indexes; + } + // Last document is incomplete; mark the document at i + 1 as the next one + return i; + } + // If we made it to the end, we want to finish counting to see if we have a full document. + switch (parser.buf[parser.structural_indexes[0]]) { + case '}': + obj_cnt--; + break; + case ']': + arr_cnt--; + break; + case '{': + obj_cnt++; + break; + case '[': + arr_cnt++; + break; + } + if (!arr_cnt && !obj_cnt) { + // We have a complete document. + return parser.n_structural_indexes; + } + return 0; +} - } // namespace stage1 - } // unnamed namespace - } // namespace lasx +} // namespace stage1 +} // unnamed namespace +} // namespace rvv_vls } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_FIND_NEXT_DOCUMENT_INDEX_H -/* end file generic/stage1/find_next_document_index.h for lasx */ -/* including generic/stage1/json_minifier.h for lasx: #include */ -/* begin file generic/stage1/json_minifier.h for lasx */ +/* end file generic/stage1/find_next_document_index.h for rvv_vls */ +/* including generic/stage1/json_minifier.h for rvv_vls: #include */ +/* begin file generic/stage1/json_minifier.h for rvv_vls */ #ifndef SIMDJSON_SRC_GENERIC_STAGE1_JSON_MINIFIER_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -50440,98 +59021,98 @@ namespace simdjson { // "simdjson/stage1.h" (this simplifies amalgation) namespace simdjson { - namespace lasx { - namespace { - namespace stage1 { +namespace rvv_vls { +namespace { +namespace stage1 { - class json_minifier { - public: - template - static error_code minify(const uint8_t* buf, size_t len, uint8_t* dst, size_t& dst_len) noexcept; +class json_minifier { +public: + template + static error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) noexcept; - private: - simdjson_inline json_minifier(uint8_t* _dst) - : dst{ _dst } - {} - template - simdjson_inline void step(const uint8_t* block_buf, buf_block_reader& reader) noexcept; - simdjson_inline void next(const simd::simd8x64& in, const json_block& block); - simdjson_inline error_code finish(uint8_t* dst_start, size_t& dst_len); - json_scanner scanner{}; - uint8_t* dst; - }; +private: + simdjson_inline json_minifier(uint8_t *_dst) + : dst{_dst} + {} + template + simdjson_inline void step(const uint8_t *block_buf, buf_block_reader &reader) noexcept; + simdjson_inline void next(const simd::simd8x64& in, const json_block& block); + simdjson_warn_unused simdjson_inline error_code finish(uint8_t *dst_start, size_t &dst_len); + json_scanner scanner{}; + uint8_t *dst; +}; - simdjson_inline void json_minifier::next(const simd::simd8x64& in, const json_block& block) { - uint64_t mask = block.whitespace(); - dst += in.compress(mask, dst); - } +simdjson_inline void json_minifier::next(const simd::simd8x64& in, const json_block& block) { + uint64_t mask = block.whitespace(); + dst += in.compress(mask, dst); +} - simdjson_inline error_code json_minifier::finish(uint8_t* dst_start, size_t& dst_len) { - error_code error = scanner.finish(); - if (error) { dst_len = 0; return error; } - dst_len = dst - dst_start; - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code json_minifier::finish(uint8_t *dst_start, size_t &dst_len) { + error_code error = scanner.finish(); + if (error) { dst_len = 0; return error; } + dst_len = dst - dst_start; + return SUCCESS; +} - template<> - simdjson_inline void json_minifier::step<128>(const uint8_t* block_buf, buf_block_reader<128>& reader) noexcept { - simd::simd8x64 in_1(block_buf); - simd::simd8x64 in_2(block_buf + 64); - json_block block_1 = scanner.next(in_1); - json_block block_2 = scanner.next(in_2); - this->next(in_1, block_1); - this->next(in_2, block_2); - reader.advance(); - } +template<> +simdjson_inline void json_minifier::step<128>(const uint8_t *block_buf, buf_block_reader<128> &reader) noexcept { + simd::simd8x64 in_1(block_buf); + simd::simd8x64 in_2(block_buf+64); + json_block block_1 = scanner.next(in_1); + json_block block_2 = scanner.next(in_2); + this->next(in_1, block_1); + this->next(in_2, block_2); + reader.advance(); +} - template<> - simdjson_inline void json_minifier::step<64>(const uint8_t* block_buf, buf_block_reader<64>& reader) noexcept { - simd::simd8x64 in_1(block_buf); - json_block block_1 = scanner.next(in_1); - this->next(block_buf, block_1); - reader.advance(); - } +template<> +simdjson_inline void json_minifier::step<64>(const uint8_t *block_buf, buf_block_reader<64> &reader) noexcept { + simd::simd8x64 in_1(block_buf); + json_block block_1 = scanner.next(in_1); + this->next(block_buf, block_1); + reader.advance(); +} - template - error_code json_minifier::minify(const uint8_t* buf, size_t len, uint8_t* dst, size_t& dst_len) noexcept { - buf_block_reader reader(buf, len); - json_minifier minifier(dst); +template +error_code json_minifier::minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) noexcept { + buf_block_reader reader(buf, len); + json_minifier minifier(dst); - // Index the first n-1 blocks - while (reader.has_full_block()) { - minifier.step(reader.full_block(), reader); - } + // Index the first n-1 blocks + while (reader.has_full_block()) { + minifier.step(reader.full_block(), reader); + } - // Index the last (remainder) block, padded with spaces - uint8_t block[STEP_SIZE]; - size_t remaining_bytes = reader.get_remainder(block); - if (remaining_bytes > 0) { - // We do not want to write directly to the output stream. Rather, we write - // to a local buffer (for safety). - uint8_t out_block[STEP_SIZE]; - uint8_t* const guarded_dst{ minifier.dst }; - minifier.dst = out_block; - minifier.step(block, reader); - size_t to_write = minifier.dst - out_block; - // In some cases, we could be enticed to consider the padded spaces - // as part of the string. This is fine as long as we do not write more - // than we consumed. - if (to_write > remaining_bytes) { to_write = remaining_bytes; } - memcpy(guarded_dst, out_block, to_write); - minifier.dst = guarded_dst + to_write; - } - return minifier.finish(dst, dst_len); - } + // Index the last (remainder) block, padded with spaces + uint8_t block[STEP_SIZE]; + size_t remaining_bytes = reader.get_remainder(block); + if (remaining_bytes > 0) { + // We do not want to write directly to the output stream. Rather, we write + // to a local buffer (for safety). + uint8_t out_block[STEP_SIZE]; + uint8_t * const guarded_dst{minifier.dst}; + minifier.dst = out_block; + minifier.step(block, reader); + size_t to_write = minifier.dst - out_block; + // In some cases, we could be enticed to consider the padded spaces + // as part of the string. This is fine as long as we do not write more + // than we consumed. + if(to_write > remaining_bytes) { to_write = remaining_bytes; } + memcpy(guarded_dst, out_block, to_write); + minifier.dst = guarded_dst + to_write; + } + return minifier.finish(dst, dst_len); +} - } // namespace stage1 - } // unnamed namespace - } // namespace lasx +} // namespace stage1 +} // unnamed namespace +} // namespace rvv_vls } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_MINIFIER_H -/* end file generic/stage1/json_minifier.h for lasx */ -/* including generic/stage1/json_structural_indexer.h for lasx: #include */ -/* begin file generic/stage1/json_structural_indexer.h for lasx */ +/* end file generic/stage1/json_minifier.h for rvv_vls */ +/* including generic/stage1/json_structural_indexer.h for rvv_vls: #include */ +/* begin file generic/stage1/json_structural_indexer.h for rvv_vls */ #ifndef SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRUCTURAL_INDEXER_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -50551,350 +59132,348 @@ namespace simdjson { // "simdjson/stage1.h" (this simplifies amalgation) namespace simdjson { - namespace lasx { - namespace { - namespace stage1 { +namespace rvv_vls { +namespace { +namespace stage1 { - class bit_indexer { - public: - uint32_t* tail; +class bit_indexer { +public: + uint32_t *tail; - simdjson_inline bit_indexer(uint32_t* index_buf) : tail(index_buf) {} + simdjson_inline bit_indexer(uint32_t *index_buf) : tail(index_buf) {} #if SIMDJSON_PREFER_REVERSE_BITS - /** - * ARM lacks a fast trailing zero instruction, but it has a fast - * bit reversal instruction and a fast leading zero instruction. - * Thus it may be profitable to reverse the bits (once) and then - * to rely on a sequence of instructions that call the leading - * zero instruction. - * - * Performance notes: - * The chosen routine is not optimal in terms of data dependency - * since zero_leading_bit might require two instructions. However, - * it tends to minimize the total number of instructions which is - * beneficial. - */ - simdjson_inline void write_index(uint32_t idx, uint64_t& rev_bits, int i) { - int lz = leading_zeroes(rev_bits); - this->tail[i] = static_cast(idx) + lz; - rev_bits = zero_leading_bit(rev_bits, lz); - } + /** + * ARM lacks a fast trailing zero instruction, but it has a fast + * bit reversal instruction and a fast leading zero instruction. + * Thus it may be profitable to reverse the bits (once) and then + * to rely on a sequence of instructions that call the leading + * zero instruction. + * + * Performance notes: + * The chosen routine is not optimal in terms of data dependency + * since zero_leading_bit might require two instructions. However, + * it tends to minimize the total number of instructions which is + * beneficial. + */ + simdjson_inline void write_index(uint32_t idx, uint64_t& rev_bits, int i) { + int lz = leading_zeroes(rev_bits); + this->tail[i] = static_cast(idx) + lz; + rev_bits = zero_leading_bit(rev_bits, lz); + } #else - /** - * Under recent x64 systems, we often have both a fast trailing zero - * instruction and a fast 'clear-lower-bit' instruction so the following - * algorithm can be competitive. - */ + /** + * Under recent x64 systems, we often have both a fast trailing zero + * instruction and a fast 'clear-lower-bit' instruction so the following + * algorithm can be competitive. + */ - simdjson_inline void write_index(uint32_t idx, uint64_t& bits, int i) { - this->tail[i] = idx + trailing_zeroes(bits); - bits = clear_lowest_bit(bits); - } + simdjson_inline void write_index(uint32_t idx, uint64_t& bits, int i) { + this->tail[i] = idx + trailing_zeroes(bits); + bits = clear_lowest_bit(bits); + } #endif // SIMDJSON_PREFER_REVERSE_BITS - template - simdjson_inline int write_indexes(uint32_t idx, uint64_t& bits) { - write_index(idx, bits, START); - SIMDJSON_IF_CONSTEXPR(N > 1) { - write_indexes<(N - 1 > 0 ? START + 1 : START), (N - 1 >= 0 ? N - 1 : 1)>(idx, bits); - } - return START + N; - } + template + simdjson_inline int write_indexes(uint32_t idx, uint64_t& bits) { + write_index(idx, bits, START); + SIMDJSON_IF_CONSTEXPR (N > 1) { + write_indexes<(N-1>0?START+1:START), (N-1>=0?N-1:1)>(idx, bits); + } + return START+N; + } - template - simdjson_inline int write_indexes_stepped(uint32_t idx, uint64_t& bits, int cnt) { - write_indexes(idx, bits); - SIMDJSON_IF_CONSTEXPR((START + STEP) < END) { - if (simdjson_unlikely((START + STEP) < cnt)) { - write_indexes_stepped<(START + STEP < END ? START + STEP : END), END, STEP>(idx, bits, cnt); - } - } - return ((END - START) % STEP) == 0 ? END : (END - START) - ((END - START) % STEP) + STEP; - } + template + simdjson_inline int write_indexes_stepped(uint32_t idx, uint64_t& bits, int cnt) { + write_indexes(idx, bits); + SIMDJSON_IF_CONSTEXPR ((START+STEP) < END) { + if (simdjson_unlikely((START+STEP) < cnt)) { + write_indexes_stepped<(START+STEP(idx, bits, cnt); + } + } + return ((END-START) % STEP) == 0 ? END : (END-START) - ((END-START) % STEP) + STEP; + } - // flatten out values in 'bits' assuming that they are are to have values of idx - // plus their position in the bitvector, and store these indexes at - // base_ptr[base] incrementing base as we go - // will potentially store extra values beyond end of valid bits, so base_ptr - // needs to be large enough to handle this - // - // If the kernel sets SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER, then it - // will provide its own version of the code. + // flatten out values in 'bits' assuming that they are are to have values of idx + // plus their position in the bitvector, and store these indexes at + // base_ptr[base] incrementing base as we go + // will potentially store extra values beyond end of valid bits, so base_ptr + // needs to be large enough to handle this + // + // If the kernel sets SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER, then it + // will provide its own version of the code. #ifdef SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER - simdjson_inline void write(uint32_t idx, uint64_t bits); + simdjson_inline void write(uint32_t idx, uint64_t bits); #else - simdjson_inline void write(uint32_t idx, uint64_t bits) { - // In some instances, the next branch is expensive because it is mispredicted. - // Unfortunately, in other cases, - // it helps tremendously. - if (bits == 0) - return; + simdjson_inline void write(uint32_t idx, uint64_t bits) { + // In some instances, the next branch is expensive because it is mispredicted. + // Unfortunately, in other cases, + // it helps tremendously. + if (bits == 0) + return; - int cnt = static_cast(count_ones(bits)); + int cnt = static_cast(count_ones(bits)); #if SIMDJSON_PREFER_REVERSE_BITS - bits = reverse_bits(bits); + bits = reverse_bits(bits); #endif #ifdef SIMDJSON_STRUCTURAL_INDEXER_STEP - static constexpr const int STEP = SIMDJSON_STRUCTURAL_INDEXER_STEP; + static constexpr const int STEP = SIMDJSON_STRUCTURAL_INDEXER_STEP; #else - static constexpr const int STEP = 4; + static constexpr const int STEP = 4; #endif - static constexpr const int STEP_UNTIL = 24; + static constexpr const int STEP_UNTIL = 24; - write_indexes_stepped<0, STEP_UNTIL, STEP>(idx, bits, cnt); - SIMDJSON_IF_CONSTEXPR(STEP_UNTIL < 64) { - if (simdjson_unlikely(STEP_UNTIL < cnt)) { - for (int i = STEP_UNTIL; i < cnt; i++) { - write_index(idx, bits, i); - } - } - } + write_indexes_stepped<0, STEP_UNTIL, STEP>(idx, bits, cnt); + SIMDJSON_IF_CONSTEXPR (STEP_UNTIL < 64) { + if (simdjson_unlikely(STEP_UNTIL < cnt)) { + for (int i=STEP_UNTIL; itail += cnt; - } + this->tail += cnt; + } #endif // SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER - }; +}; - class json_structural_indexer { - public: - /** - * Find the important bits of JSON in a 128-byte chunk, and add them to structural_indexes. - * - * @param partial Setting the partial parameter to true allows the find_structural_bits to - * tolerate unclosed strings. The caller should still ensure that the input is valid UTF-8. If - * you are processing substrings, you may want to call on a function like trimmed_length_safe_utf8. - */ - template - static error_code index(const uint8_t* buf, size_t len, dom_parser_implementation& parser, stage1_mode partial) noexcept; +class json_structural_indexer { +public: + /** + * Find the important bits of JSON in a 128-byte chunk, and add them to structural_indexes. + * + * @param partial Setting the partial parameter to true allows the find_structural_bits to + * tolerate unclosed strings. The caller should still ensure that the input is valid UTF-8. If + * you are processing substrings, you may want to call on a function like trimmed_length_safe_utf8. + */ + template + static error_code index(const uint8_t *buf, size_t len, dom_parser_implementation &parser, stage1_mode partial) noexcept; - private: - simdjson_inline json_structural_indexer(uint32_t* structural_indexes); - template - simdjson_inline void step(const uint8_t* block, buf_block_reader& reader) noexcept; - simdjson_inline void next(const simd::simd8x64& in, const json_block& block, size_t idx); - simdjson_inline error_code finish(dom_parser_implementation& parser, size_t idx, size_t len, stage1_mode partial); +private: + simdjson_inline json_structural_indexer(uint32_t *structural_indexes); + template + simdjson_inline void step(const uint8_t *block, buf_block_reader &reader) noexcept; + simdjson_inline void next(const simd::simd8x64& in, const json_block& block, size_t idx); + simdjson_warn_unused simdjson_inline error_code finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial); - json_scanner scanner{}; - utf8_checker checker{}; - bit_indexer indexer; - uint64_t prev_structurals = 0; - uint64_t unescaped_chars_error = 0; - }; + json_scanner scanner{}; + utf8_checker checker{}; + bit_indexer indexer; + uint64_t prev_structurals = 0; + uint64_t unescaped_chars_error = 0; +}; - simdjson_inline json_structural_indexer::json_structural_indexer(uint32_t* structural_indexes) : indexer{ structural_indexes } {} +simdjson_inline json_structural_indexer::json_structural_indexer(uint32_t *structural_indexes) : indexer{structural_indexes} {} - // Skip the last character if it is partial - simdjson_inline size_t trim_partial_utf8(const uint8_t* buf, size_t len) { - if (simdjson_unlikely(len < 3)) { - switch (len) { - case 2: - if (buf[len - 1] >= 0xc0) { return len - 1; } // 2-, 3- and 4-byte characters with only 1 byte left - if (buf[len - 2] >= 0xe0) { return len - 2; } // 3- and 4-byte characters with only 2 bytes left - return len; - case 1: - if (buf[len - 1] >= 0xc0) { return len - 1; } // 2-, 3- and 4-byte characters with only 1 byte left - return len; - case 0: - return len; - } - } - if (buf[len - 1] >= 0xc0) { return len - 1; } // 2-, 3- and 4-byte characters with only 1 byte left - if (buf[len - 2] >= 0xe0) { return len - 2; } // 3- and 4-byte characters with only 1 byte left - if (buf[len - 3] >= 0xf0) { return len - 3; } // 4-byte characters with only 3 bytes left - return len; - } +// Skip the last character if it is partial +simdjson_inline size_t trim_partial_utf8(const uint8_t *buf, size_t len) { + if (simdjson_unlikely(len < 3)) { + switch (len) { + case 2: + if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left + if (buf[len-2] >= 0xe0) { return len-2; } // 3- and 4-byte characters with only 2 bytes left + return len; + case 1: + if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left + return len; + case 0: + return len; + } + } + if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left + if (buf[len-2] >= 0xe0) { return len-2; } // 3- and 4-byte characters with only 1 byte left + if (buf[len-3] >= 0xf0) { return len-3; } // 4-byte characters with only 3 bytes left + return len; +} - // - // PERF NOTES: - // We pipe 2 inputs through these stages: - // 1. Load JSON into registers. This takes a long time and is highly parallelizable, so we load - // 2 inputs' worth at once so that by the time step 2 is looking for them input, it's available. - // 2. Scan the JSON for critical data: strings, scalars and operators. This is the critical path. - // The output of step 1 depends entirely on this information. These functions don't quite use - // up enough CPU: the second half of the functions is highly serial, only using 1 execution core - // at a time. The second input's scans has some dependency on the first ones finishing it, but - // they can make a lot of progress before they need that information. - // 3. Step 1 does not use enough capacity, so we run some extra stuff while we're waiting for that - // to finish: utf-8 checks and generating the output from the last iteration. - // - // The reason we run 2 inputs at a time, is steps 2 and 3 are *still* not enough to soak up all - // available capacity with just one input. Running 2 at a time seems to give the CPU a good enough - // workout. - // - template - error_code json_structural_indexer::index(const uint8_t* buf, size_t len, dom_parser_implementation& parser, stage1_mode partial) noexcept { - if (simdjson_unlikely(len > parser.capacity())) { return CAPACITY; } - // We guard the rest of the code so that we can assume that len > 0 throughout. - if (len == 0) { return EMPTY; } - if (is_streaming(partial)) { - len = trim_partial_utf8(buf, len); - // If you end up with an empty window after trimming - // the partial UTF-8 bytes, then chances are good that you - // have an UTF-8 formatting error. - if (len == 0) { return UTF8_ERROR; } - } - buf_block_reader reader(buf, len); - json_structural_indexer indexer(parser.structural_indexes.get()); +// +// PERF NOTES: +// We pipe 2 inputs through these stages: +// 1. Load JSON into registers. This takes a long time and is highly parallelizable, so we load +// 2 inputs' worth at once so that by the time step 2 is looking for them input, it's available. +// 2. Scan the JSON for critical data: strings, scalars and operators. This is the critical path. +// The output of step 1 depends entirely on this information. These functions don't quite use +// up enough CPU: the second half of the functions is highly serial, only using 1 execution core +// at a time. The second input's scans has some dependency on the first ones finishing it, but +// they can make a lot of progress before they need that information. +// 3. Step 1 does not use enough capacity, so we run some extra stuff while we're waiting for that +// to finish: utf-8 checks and generating the output from the last iteration. +// +// The reason we run 2 inputs at a time, is steps 2 and 3 are *still* not enough to soak up all +// available capacity with just one input. Running 2 at a time seems to give the CPU a good enough +// workout. +// +template +error_code json_structural_indexer::index(const uint8_t *buf, size_t len, dom_parser_implementation &parser, stage1_mode partial) noexcept { + if (simdjson_unlikely(len > parser.capacity())) { return CAPACITY; } + // We guard the rest of the code so that we can assume that len > 0 throughout. + if (len == 0) { return EMPTY; } + if (is_streaming(partial)) { + len = trim_partial_utf8(buf, len); + // If you end up with an empty window after trimming + // the partial UTF-8 bytes, then chances are good that you + // have an UTF-8 formatting error. + if(len == 0) { return UTF8_ERROR; } + } + buf_block_reader reader(buf, len); + json_structural_indexer indexer(parser.structural_indexes.get()); - // Read all but the last block - while (reader.has_full_block()) { - indexer.step(reader.full_block(), reader); - } - // Take care of the last block (will always be there unless file is empty which is - // not supposed to happen.) - uint8_t block[STEP_SIZE]; - if (simdjson_unlikely(reader.get_remainder(block) == 0)) { return UNEXPECTED_ERROR; } - indexer.step(block, reader); - return indexer.finish(parser, reader.block_index(), len, partial); - } + // Read all but the last block + while (reader.has_full_block()) { + indexer.step(reader.full_block(), reader); + } + // Take care of the last block (will always be there unless file is empty which is + // not supposed to happen.) + uint8_t block[STEP_SIZE]; + if (simdjson_unlikely(reader.get_remainder(block) == 0)) { return UNEXPECTED_ERROR; } + indexer.step(block, reader); + return indexer.finish(parser, reader.block_index(), len, partial); +} - template<> - simdjson_inline void json_structural_indexer::step<128>(const uint8_t* block, buf_block_reader<128>& reader) noexcept { - simd::simd8x64 in_1(block); - simd::simd8x64 in_2(block + 64); - json_block block_1 = scanner.next(in_1); - json_block block_2 = scanner.next(in_2); - this->next(in_1, block_1, reader.block_index()); - this->next(in_2, block_2, reader.block_index() + 64); - reader.advance(); - } +template<> +simdjson_inline void json_structural_indexer::step<128>(const uint8_t *block, buf_block_reader<128> &reader) noexcept { + simd::simd8x64 in_1(block); + simd::simd8x64 in_2(block+64); + json_block block_1 = scanner.next(in_1); + json_block block_2 = scanner.next(in_2); + this->next(in_1, block_1, reader.block_index()); + this->next(in_2, block_2, reader.block_index()+64); + reader.advance(); +} - template<> - simdjson_inline void json_structural_indexer::step<64>(const uint8_t* block, buf_block_reader<64>& reader) noexcept { - simd::simd8x64 in_1(block); - json_block block_1 = scanner.next(in_1); - this->next(in_1, block_1, reader.block_index()); - reader.advance(); - } +template<> +simdjson_inline void json_structural_indexer::step<64>(const uint8_t *block, buf_block_reader<64> &reader) noexcept { + simd::simd8x64 in_1(block); + json_block block_1 = scanner.next(in_1); + this->next(in_1, block_1, reader.block_index()); + reader.advance(); +} - simdjson_inline void json_structural_indexer::next(const simd::simd8x64& in, const json_block& block, size_t idx) { - uint64_t unescaped = in.lteq(0x1F); +simdjson_inline void json_structural_indexer::next(const simd::simd8x64& in, const json_block& block, size_t idx) { + uint64_t unescaped = in.lteq(0x1F); #if SIMDJSON_UTF8VALIDATION - checker.check_next_input(in); + checker.check_next_input(in); #endif - indexer.write(uint32_t(idx - 64), prev_structurals); // Output *last* iteration's structurals to the parser - prev_structurals = block.structural_start(); - unescaped_chars_error |= block.non_quote_inside_string(unescaped); - } + indexer.write(uint32_t(idx-64), prev_structurals); // Output *last* iteration's structurals to the parser + prev_structurals = block.structural_start(); + unescaped_chars_error |= block.non_quote_inside_string(unescaped); +} - simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementation& parser, size_t idx, size_t len, stage1_mode partial) { - // Write out the final iteration's structurals - indexer.write(uint32_t(idx - 64), prev_structurals); - error_code error = scanner.finish(); - // We deliberately break down the next expression so that it is - // human readable. - const bool should_we_exit = is_streaming(partial) ? - ((error != SUCCESS) && (error != UNCLOSED_STRING)) // when partial we tolerate UNCLOSED_STRING - : (error != SUCCESS); // if partial is false, we must have SUCCESS - const bool have_unclosed_string = (error == UNCLOSED_STRING); - if (simdjson_unlikely(should_we_exit)) { return error; } +simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial) { + // Write out the final iteration's structurals + indexer.write(uint32_t(idx-64), prev_structurals); + error_code error = scanner.finish(); + // We deliberately break down the next expression so that it is + // human readable. + const bool should_we_exit = is_streaming(partial) ? + ((error != SUCCESS) && (error != UNCLOSED_STRING)) // when partial we tolerate UNCLOSED_STRING + : (error != SUCCESS); // if partial is false, we must have SUCCESS + const bool have_unclosed_string = (error == UNCLOSED_STRING); + if (simdjson_unlikely(should_we_exit)) { return error; } - if (unescaped_chars_error) { - return UNESCAPED_CHARS; - } - parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get()); - /*** - * The On-Demand API requires special padding. - * - * This is related to https://github.com/simdjson/simdjson/issues/906 - * Basically, we want to make sure that if the parsing continues beyond the last (valid) - * structural character, it quickly stops. - * Only three structural characters can be repeated without triggering an error in JSON: [,] and }. - * We repeat the padding character (at 'len'). We don't know what it is, but if the parsing - * continues, then it must be [,] or }. - * Suppose it is ] or }. We backtrack to the first character, what could it be that would - * not trigger an error? It could be ] or } but no, because you can't start a document that way. - * It can't be a comma, a colon or any simple value. So the only way we could continue is - * if the repeated character is [. But if so, the document must start with [. But if the document - * starts with [, it should end with ]. If we enforce that rule, then we would get - * ][[ which is invalid. - * - * This is illustrated with the test array_iterate_unclosed_error() on the following input: - * R"({ "a": [,,)" - **/ - parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); // used later in partial == stage1_mode::streaming_final - parser.structural_indexes[parser.n_structural_indexes + 1] = uint32_t(len); - parser.structural_indexes[parser.n_structural_indexes + 2] = 0; - parser.next_structural_index = 0; - // a valid JSON file cannot have zero structural indexes - we should have found something - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { - return EMPTY; - } - if (simdjson_unlikely(parser.structural_indexes[parser.n_structural_indexes - 1] > len)) { - return UNEXPECTED_ERROR; - } - if (partial == stage1_mode::streaming_partial) { - // If we have an unclosed string, then the last structural - // will be the quote and we want to make sure to omit it. - if (have_unclosed_string) { - parser.n_structural_indexes--; - // a valid JSON file cannot have zero structural indexes - we should have found something - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; } - } - // We truncate the input to the end of the last complete document (or zero). - auto new_structural_indexes = find_next_document_index(parser); - if (new_structural_indexes == 0 && parser.n_structural_indexes > 0) { - if (parser.structural_indexes[0] == 0) { - // If the buffer is partial and we started at index 0 but the document is - // incomplete, it's too big to parse. - return CAPACITY; - } - else { - // It is possible that the document could be parsed, we just had a lot - // of white space. - parser.n_structural_indexes = 0; - return EMPTY; - } - } + if (unescaped_chars_error) { + return UNESCAPED_CHARS; + } + parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get()); + /*** + * The On-Demand API requires special padding. + * + * This is related to https://github.com/simdjson/simdjson/issues/906 + * Basically, we want to make sure that if the parsing continues beyond the last (valid) + * structural character, it quickly stops. + * Only three structural characters can be repeated without triggering an error in JSON: [,] and }. + * We repeat the padding character (at 'len'). We don't know what it is, but if the parsing + * continues, then it must be [,] or }. + * Suppose it is ] or }. We backtrack to the first character, what could it be that would + * not trigger an error? It could be ] or } but no, because you can't start a document that way. + * It can't be a comma, a colon or any simple value. So the only way we could continue is + * if the repeated character is [. But if so, the document must start with [. But if the document + * starts with [, it should end with ]. If we enforce that rule, then we would get + * ][[ which is invalid. + * + * This is illustrated with the test array_iterate_unclosed_error() on the following input: + * R"({ "a": [,,)" + **/ + parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); // used later in partial == stage1_mode::streaming_final + parser.structural_indexes[parser.n_structural_indexes + 1] = uint32_t(len); + parser.structural_indexes[parser.n_structural_indexes + 2] = 0; + parser.next_structural_index = 0; + // a valid JSON file cannot have zero structural indexes - we should have found something + if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { + return EMPTY; + } + if (simdjson_unlikely(parser.structural_indexes[parser.n_structural_indexes - 1] > len)) { + return UNEXPECTED_ERROR; + } + if (partial == stage1_mode::streaming_partial) { + // If we have an unclosed string, then the last structural + // will be the quote and we want to make sure to omit it. + if(have_unclosed_string) { + parser.n_structural_indexes--; + // a valid JSON file cannot have zero structural indexes - we should have found something + if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; } + } + // We truncate the input to the end of the last complete document (or zero). + auto new_structural_indexes = find_next_document_index(parser); + if (new_structural_indexes == 0 && parser.n_structural_indexes > 0) { + if(parser.structural_indexes[0] == 0) { + // If the buffer is partial and we started at index 0 but the document is + // incomplete, it's too big to parse. + return CAPACITY; + } else { + // It is possible that the document could be parsed, we just had a lot + // of white space. + parser.n_structural_indexes = 0; + return EMPTY; + } + } - parser.n_structural_indexes = new_structural_indexes; - } - else if (partial == stage1_mode::streaming_final) { - if (have_unclosed_string) { parser.n_structural_indexes--; } - // We truncate the input to the end of the last complete document (or zero). - // Because partial == stage1_mode::streaming_final, it means that we may - // silently ignore trailing garbage. Though it sounds bad, we do it - // deliberately because many people who have streams of JSON documents - // will truncate them for processing. E.g., imagine that you are uncompressing - // the data from a size file or receiving it in chunks from the network. You - // may not know where exactly the last document will be. Meanwhile the - // document_stream instances allow people to know the JSON documents they are - // parsing (see the iterator.source() method). - parser.n_structural_indexes = find_next_document_index(parser); - // We store the initial n_structural_indexes so that the client can see - // whether we used truncation. If initial_n_structural_indexes == parser.n_structural_indexes, - // then this will query parser.structural_indexes[parser.n_structural_indexes] which is len, - // otherwise, it will copy some prior index. - parser.structural_indexes[parser.n_structural_indexes + 1] = parser.structural_indexes[parser.n_structural_indexes]; - // This next line is critical, do not change it unless you understand what you are - // doing. - parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { - // We tolerate an unclosed string at the very end of the stream. Indeed, users - // often load their data in bulk without being careful and they want us to ignore - // the trailing garbage. - return EMPTY; - } - } - checker.check_eof(); - return checker.errors(); - } + parser.n_structural_indexes = new_structural_indexes; + } else if (partial == stage1_mode::streaming_final) { + if(have_unclosed_string) { parser.n_structural_indexes--; } + // We truncate the input to the end of the last complete document (or zero). + // Because partial == stage1_mode::streaming_final, it means that we may + // silently ignore trailing garbage. Though it sounds bad, we do it + // deliberately because many people who have streams of JSON documents + // will truncate them for processing. E.g., imagine that you are uncompressing + // the data from a size file or receiving it in chunks from the network. You + // may not know where exactly the last document will be. Meanwhile the + // document_stream instances allow people to know the JSON documents they are + // parsing (see the iterator.source() method). + parser.n_structural_indexes = find_next_document_index(parser); + // We store the initial n_structural_indexes so that the client can see + // whether we used truncation. If initial_n_structural_indexes == parser.n_structural_indexes, + // then this will query parser.structural_indexes[parser.n_structural_indexes] which is len, + // otherwise, it will copy some prior index. + parser.structural_indexes[parser.n_structural_indexes + 1] = parser.structural_indexes[parser.n_structural_indexes]; + // This next line is critical, do not change it unless you understand what you are + // doing. + parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); + if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { + // We tolerate an unclosed string at the very end of the stream. Indeed, users + // often load their data in bulk without being careful and they want us to ignore + // the trailing garbage. + return EMPTY; + } + } + checker.check_eof(); + return checker.errors(); +} - } // namespace stage1 - } // unnamed namespace - } // namespace lasx +} // namespace stage1 +} // unnamed namespace +} // namespace rvv_vls } // namespace simdjson // Clear CUSTOM_BIT_INDEXER so other implementations can set it if they need to. #undef SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER #endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRUCTURAL_INDEXER_H -/* end file generic/stage1/json_structural_indexer.h for lasx */ -/* including generic/stage1/utf8_validator.h for lasx: #include */ -/* begin file generic/stage1/utf8_validator.h for lasx */ +/* end file generic/stage1/json_structural_indexer.h for rvv_vls */ +/* including generic/stage1/utf8_validator.h for rvv_vls: #include */ +/* begin file generic/stage1/utf8_validator.h for rvv_vls */ #ifndef SIMDJSON_SRC_GENERIC_STAGE1_UTF8_VALIDATOR_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -50905,48 +59484,48 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace { - namespace stage1 { +namespace rvv_vls { +namespace { +namespace stage1 { - /** - * Validates that the string is actual UTF-8. - */ - template - bool generic_validate_utf8(const uint8_t* input, size_t length) { - checker c{}; - buf_block_reader<64> reader(input, length); - while (reader.has_full_block()) { - simd::simd8x64 in(reader.full_block()); - c.check_next_input(in); - reader.advance(); - } - uint8_t block[64]{}; - reader.get_remainder(block); - simd::simd8x64 in(block); - c.check_next_input(in); - reader.advance(); - c.check_eof(); - return c.errors() == error_code::SUCCESS; - } +/** + * Validates that the string is actual UTF-8. + */ +template +bool generic_validate_utf8(const uint8_t * input, size_t length) { + checker c{}; + buf_block_reader<64> reader(input, length); + while (reader.has_full_block()) { + simd::simd8x64 in(reader.full_block()); + c.check_next_input(in); + reader.advance(); + } + uint8_t block[64]{}; + reader.get_remainder(block); + simd::simd8x64 in(block); + c.check_next_input(in); + reader.advance(); + c.check_eof(); + return c.errors() == error_code::SUCCESS; +} - bool generic_validate_utf8(const char* input, size_t length) { - return generic_validate_utf8(reinterpret_cast(input), length); - } +bool generic_validate_utf8(const char * input, size_t length) { + return generic_validate_utf8(reinterpret_cast(input),length); +} - } // namespace stage1 - } // unnamed namespace - } // namespace lasx +} // namespace stage1 +} // unnamed namespace +} // namespace rvv_vls } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_UTF8_VALIDATOR_H -/* end file generic/stage1/utf8_validator.h for lasx */ -/* end file generic/stage1/amalgamated.h for lasx */ -/* including generic/stage2/amalgamated.h for lasx: #include */ -/* begin file generic/stage2/amalgamated.h for lasx */ +/* end file generic/stage1/utf8_validator.h for rvv_vls */ +/* end file generic/stage1/amalgamated.h for rvv_vls */ +/* including generic/stage2/amalgamated.h for rvv_vls: #include */ +/* begin file generic/stage2/amalgamated.h for rvv_vls */ // Stuff other things depend on -/* including generic/stage2/base.h for lasx: #include */ -/* begin file generic/stage2/base.h for lasx */ +/* including generic/stage2/base.h for rvv_vls: #include */ +/* begin file generic/stage2/base.h for rvv_vls */ #ifndef SIMDJSON_SRC_GENERIC_STAGE2_BASE_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -50955,24 +59534,24 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace { - namespace stage2 { +namespace rvv_vls { +namespace { +namespace stage2 { - class json_iterator; - class structural_iterator; - struct tape_builder; - struct tape_writer; +class json_iterator; +class structural_iterator; +struct tape_builder; +struct tape_writer; - } // namespace stage2 - } // unnamed namespace - } // namespace lasx +} // namespace stage2 +} // unnamed namespace +} // namespace rvv_vls } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_BASE_H -/* end file generic/stage2/base.h for lasx */ -/* including generic/stage2/tape_writer.h for lasx: #include */ -/* begin file generic/stage2/tape_writer.h for lasx */ +/* end file generic/stage2/base.h for rvv_vls */ +/* including generic/stage2/tape_writer.h for rvv_vls: #include */ +/* begin file generic/stage2/tape_writer.h for rvv_vls */ #ifndef SIMDJSON_SRC_GENERIC_STAGE2_TAPE_WRITER_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -50984,115 +59563,115 @@ namespace simdjson { #include namespace simdjson { - namespace lasx { - namespace { - namespace stage2 { +namespace rvv_vls { +namespace { +namespace stage2 { - struct tape_writer { - /** The next place to write to tape */ - uint64_t* next_tape_loc; +struct tape_writer { + /** The next place to write to tape */ + uint64_t *next_tape_loc; - /** Write a signed 64-bit value to tape. */ - simdjson_inline void append_s64(int64_t value) noexcept; + /** Write a signed 64-bit value to tape. */ + simdjson_inline void append_s64(int64_t value) noexcept; - /** Write an unsigned 64-bit value to tape. */ - simdjson_inline void append_u64(uint64_t value) noexcept; + /** Write an unsigned 64-bit value to tape. */ + simdjson_inline void append_u64(uint64_t value) noexcept; - /** Write a double value to tape. */ - simdjson_inline void append_double(double value) noexcept; + /** Write a double value to tape. */ + simdjson_inline void append_double(double value) noexcept; - /** - * Append a tape entry (an 8-bit type,and 56 bits worth of value). - */ - simdjson_inline void append(uint64_t val, internal::tape_type t) noexcept; + /** + * Append a tape entry (an 8-bit type,and 56 bits worth of value). + */ + simdjson_inline void append(uint64_t val, internal::tape_type t) noexcept; - /** - * Skip the current tape entry without writing. - * - * Used to skip the start of the container, since we'll come back later to fill it in when the - * container ends. - */ - simdjson_inline void skip() noexcept; + /** + * Skip the current tape entry without writing. + * + * Used to skip the start of the container, since we'll come back later to fill it in when the + * container ends. + */ + simdjson_inline void skip() noexcept; - /** - * Skip the number of tape entries necessary to write a large u64 or i64. - */ - simdjson_inline void skip_large_integer() noexcept; + /** + * Skip the number of tape entries necessary to write a large u64 or i64. + */ + simdjson_inline void skip_large_integer() noexcept; - /** - * Skip the number of tape entries necessary to write a double. - */ - simdjson_inline void skip_double() noexcept; + /** + * Skip the number of tape entries necessary to write a double. + */ + simdjson_inline void skip_double() noexcept; - /** - * Write a value to a known location on tape. - * - * Used to go back and write out the start of a container after the container ends. - */ - simdjson_inline static void write(uint64_t& tape_loc, uint64_t val, internal::tape_type t) noexcept; + /** + * Write a value to a known location on tape. + * + * Used to go back and write out the start of a container after the container ends. + */ + simdjson_inline static void write(uint64_t &tape_loc, uint64_t val, internal::tape_type t) noexcept; - private: - /** - * Append both the tape entry, and a supplementary value following it. Used for types that need - * all 64 bits, such as double and uint64_t. - */ - template - simdjson_inline void append2(uint64_t val, T val2, internal::tape_type t) noexcept; - }; // struct tape_writer +private: + /** + * Append both the tape entry, and a supplementary value following it. Used for types that need + * all 64 bits, such as double and uint64_t. + */ + template + simdjson_inline void append2(uint64_t val, T val2, internal::tape_type t) noexcept; +}; // struct tape_writer - simdjson_inline void tape_writer::append_s64(int64_t value) noexcept { - append2(0, value, internal::tape_type::INT64); - } +simdjson_inline void tape_writer::append_s64(int64_t value) noexcept { + append2(0, value, internal::tape_type::INT64); +} - simdjson_inline void tape_writer::append_u64(uint64_t value) noexcept { - append(0, internal::tape_type::UINT64); - *next_tape_loc = value; - next_tape_loc++; - } +simdjson_inline void tape_writer::append_u64(uint64_t value) noexcept { + append(0, internal::tape_type::UINT64); + *next_tape_loc = value; + next_tape_loc++; +} - /** Write a double value to tape. */ - simdjson_inline void tape_writer::append_double(double value) noexcept { - append2(0, value, internal::tape_type::DOUBLE); - } +/** Write a double value to tape. */ +simdjson_inline void tape_writer::append_double(double value) noexcept { + append2(0, value, internal::tape_type::DOUBLE); +} - simdjson_inline void tape_writer::skip() noexcept { - next_tape_loc++; - } +simdjson_inline void tape_writer::skip() noexcept { + next_tape_loc++; +} - simdjson_inline void tape_writer::skip_large_integer() noexcept { - next_tape_loc += 2; - } +simdjson_inline void tape_writer::skip_large_integer() noexcept { + next_tape_loc += 2; +} - simdjson_inline void tape_writer::skip_double() noexcept { - next_tape_loc += 2; - } +simdjson_inline void tape_writer::skip_double() noexcept { + next_tape_loc += 2; +} - simdjson_inline void tape_writer::append(uint64_t val, internal::tape_type t) noexcept { - *next_tape_loc = val | ((uint64_t(char(t))) << 56); - next_tape_loc++; - } +simdjson_inline void tape_writer::append(uint64_t val, internal::tape_type t) noexcept { + *next_tape_loc = val | ((uint64_t(char(t))) << 56); + next_tape_loc++; +} - template - simdjson_inline void tape_writer::append2(uint64_t val, T val2, internal::tape_type t) noexcept { - append(val, t); - static_assert(sizeof(val2) == sizeof(*next_tape_loc), "Type is not 64 bits!"); - memcpy(next_tape_loc, &val2, sizeof(val2)); - next_tape_loc++; - } +template +simdjson_inline void tape_writer::append2(uint64_t val, T val2, internal::tape_type t) noexcept { + append(val, t); + static_assert(sizeof(val2) == sizeof(*next_tape_loc), "Type is not 64 bits!"); + memcpy(next_tape_loc, &val2, sizeof(val2)); + next_tape_loc++; +} - simdjson_inline void tape_writer::write(uint64_t& tape_loc, uint64_t val, internal::tape_type t) noexcept { - tape_loc = val | ((uint64_t(char(t))) << 56); - } +simdjson_inline void tape_writer::write(uint64_t &tape_loc, uint64_t val, internal::tape_type t) noexcept { + tape_loc = val | ((uint64_t(char(t))) << 56); +} - } // namespace stage2 - } // unnamed namespace - } // namespace lasx +} // namespace stage2 +} // unnamed namespace +} // namespace rvv_vls } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_WRITER_H -/* end file generic/stage2/tape_writer.h for lasx */ -/* including generic/stage2/logger.h for lasx: #include */ -/* begin file generic/stage2/logger.h for lasx */ +/* end file generic/stage2/tape_writer.h for rvv_vls */ +/* including generic/stage2/logger.h for rvv_vls: #include */ +/* begin file generic/stage2/logger.h for rvv_vls */ #ifndef SIMDJSON_SRC_GENERIC_STAGE2_LOGGER_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -51106,100 +59685,98 @@ namespace simdjson { // This is for an internal-only stage 2 specific logger. // Set LOG_ENABLED = true to log what stage 2 is doing! namespace simdjson { - namespace lasx { - namespace { - namespace logger { +namespace rvv_vls { +namespace { +namespace logger { - static constexpr const char* DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"; + static constexpr const char * DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"; #if SIMDJSON_VERBOSE_LOGGING - static constexpr const bool LOG_ENABLED = true; + static constexpr const bool LOG_ENABLED = true; #else - static constexpr const bool LOG_ENABLED = false; + static constexpr const bool LOG_ENABLED = false; #endif - 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 constexpr const int LOG_INDEX_LEN = 5; + 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 constexpr const int LOG_INDEX_LEN = 5; - static int log_depth; // Not threadsafe. Log only. + static int log_depth; // Not threadsafe. Log only. - // Helper to turn unprintable or newline characters into spaces - static simdjson_inline char printable_char(char c) { - if (c >= 0x20) { - return c; - } - else { - return ' '; - } - } + // Helper to turn unprintable or newline characters into spaces + static simdjson_inline char printable_char(char c) { + if (c >= 0x20) { + return c; + } else { + return ' '; + } + } - // Print the header and set up log_start - static simdjson_inline void log_start() { - if (LOG_ENABLED) { - log_depth = 0; - printf("\n"); - printf("| %-*s | %-*s | %-*s | %-*s | Detail |\n", LOG_EVENT_LEN, "Event", LOG_BUFFER_LEN, "Buffer", LOG_SMALL_BUFFER_LEN, "Next", 5, "Next#"); - printf("|%.*s|%.*s|%.*s|%.*s|--------|\n", LOG_EVENT_LEN + 2, DASHES, LOG_BUFFER_LEN + 2, DASHES, LOG_SMALL_BUFFER_LEN + 2, DASHES, 5 + 2, DASHES); - } - } + // Print the header and set up log_start + static simdjson_inline void log_start() { + if (LOG_ENABLED) { + log_depth = 0; + printf("\n"); + printf("| %-*s | %-*s | %-*s | %-*s | Detail |\n", LOG_EVENT_LEN, "Event", LOG_BUFFER_LEN, "Buffer", LOG_SMALL_BUFFER_LEN, "Next", 5, "Next#"); + printf("|%.*s|%.*s|%.*s|%.*s|--------|\n", LOG_EVENT_LEN+2, DASHES, LOG_BUFFER_LEN+2, DASHES, LOG_SMALL_BUFFER_LEN+2, DASHES, 5+2, DASHES); + } + } - simdjson_unused static simdjson_inline void log_string(const char* message) { - if (LOG_ENABLED) { - printf("%s\n", message); - } - } + simdjson_unused static simdjson_inline void log_string(const char *message) { + if (LOG_ENABLED) { + printf("%s\n", message); + } + } - // Logs a single line from the stage 2 DOM parser - template - static simdjson_inline void log_line(S& structurals, const char* title_prefix, const char* title, const char* detail) { - if (LOG_ENABLED) { - printf("| %*s%s%-*s ", log_depth * 2, "", title_prefix, LOG_EVENT_LEN - log_depth * 2 - int(strlen(title_prefix)), title); - auto current_index = structurals.at_beginning() ? nullptr : structurals.next_structural - 1; - auto next_index = structurals.next_structural; - auto current = current_index ? &structurals.buf[*current_index] : reinterpret_cast(" "); - auto next = &structurals.buf[*next_index]; - { - // Print the next N characters in the buffer. - printf("| "); - // Otherwise, print the characters starting from the buffer position. - // Print spaces for unprintable or newline characters. - for (int i = 0; i < LOG_BUFFER_LEN; i++) { - printf("%c", printable_char(current[i])); - } - printf(" "); - // Print the next N characters in the buffer. - printf("| "); - // Otherwise, print the characters starting from the buffer position. - // Print spaces for unprintable or newline characters. - for (int i = 0; i < LOG_SMALL_BUFFER_LEN; i++) { - printf("%c", printable_char(next[i])); - } - printf(" "); - } - if (current_index) { - printf("| %*u ", LOG_INDEX_LEN, *current_index); - } - else { - printf("| %-*s ", LOG_INDEX_LEN, ""); - } - // printf("| %*u ", LOG_INDEX_LEN, structurals.next_tape_index()); - printf("| %-s ", detail); - printf("|\n"); - } - } + // Logs a single line from the stage 2 DOM parser + template + static simdjson_inline void log_line(S &structurals, const char *title_prefix, const char *title, const char *detail) { + if (LOG_ENABLED) { + printf("| %*s%s%-*s ", log_depth*2, "", title_prefix, LOG_EVENT_LEN - log_depth*2 - int(strlen(title_prefix)), title); + auto current_index = structurals.at_beginning() ? nullptr : structurals.next_structural-1; + auto next_index = structurals.next_structural; + auto current = current_index ? &structurals.buf[*current_index] : reinterpret_cast(" "); + auto next = &structurals.buf[*next_index]; + { + // Print the next N characters in the buffer. + printf("| "); + // Otherwise, print the characters starting from the buffer position. + // Print spaces for unprintable or newline characters. + for (int i=0;i */ -/* begin file generic/stage2/json_iterator.h for lasx */ +/* including generic/stage2/json_iterator.h for rvv_vls: #include */ +/* begin file generic/stage2/json_iterator.h for rvv_vls */ #ifndef SIMDJSON_SRC_GENERIC_STAGE2_JSON_ITERATOR_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -51210,328 +59787,328 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace { - namespace stage2 { +namespace rvv_vls { +namespace { +namespace stage2 { - class json_iterator { - public: - const uint8_t* const buf; - uint32_t* next_structural; - dom_parser_implementation& dom_parser; - uint32_t depth{ 0 }; +class json_iterator { +public: + const uint8_t* const buf; + uint32_t *next_structural; + dom_parser_implementation &dom_parser; + uint32_t depth{0}; - /** - * Walk the JSON document. - * - * The visitor receives callbacks when values are encountered. All callbacks pass the iterator as - * the first parameter; some callbacks have other parameters as well: - * - * - visit_document_start() - at the beginning. - * - visit_document_end() - at the end (if things were successful). - * - * - visit_array_start() - at the start `[` of a non-empty array. - * - visit_array_end() - at the end `]` of a non-empty array. - * - visit_empty_array() - when an empty array is encountered. - * - * - visit_object_end() - at the start `]` of a non-empty object. - * - visit_object_start() - at the end `]` of a non-empty object. - * - visit_empty_object() - when an empty object is encountered. - * - visit_key(const uint8_t *key) - when a key in an object field is encountered. key is - * guaranteed to point at the first quote of the string (`"key"`). - * - visit_primitive(const uint8_t *value) - when a value is a string, number, boolean or null. - * - visit_root_primitive(iter, uint8_t *value) - when the top-level value is a string, number, boolean or null. - * - * - increment_count(iter) - each time a value is found in an array or object. - */ - template - simdjson_warn_unused simdjson_inline error_code walk_document(V& visitor) noexcept; + /** + * Walk the JSON document. + * + * The visitor receives callbacks when values are encountered. All callbacks pass the iterator as + * the first parameter; some callbacks have other parameters as well: + * + * - visit_document_start() - at the beginning. + * - visit_document_end() - at the end (if things were successful). + * + * - visit_array_start() - at the start `[` of a non-empty array. + * - visit_array_end() - at the end `]` of a non-empty array. + * - visit_empty_array() - when an empty array is encountered. + * + * - visit_object_end() - at the start `]` of a non-empty object. + * - visit_object_start() - at the end `]` of a non-empty object. + * - visit_empty_object() - when an empty object is encountered. + * - visit_key(const uint8_t *key) - when a key in an object field is encountered. key is + * guaranteed to point at the first quote of the string (`"key"`). + * - visit_primitive(const uint8_t *value) - when a value is a string, number, boolean or null. + * - visit_root_primitive(iter, uint8_t *value) - when the top-level value is a string, number, boolean or null. + * + * - increment_count(iter) - each time a value is found in an array or object. + */ + template + simdjson_warn_unused simdjson_inline error_code walk_document(V &visitor) noexcept; - /** - * Create an iterator capable of walking a JSON document. - * - * The document must have already passed through stage 1. - */ - simdjson_inline json_iterator(dom_parser_implementation& _dom_parser, size_t start_structural_index); + /** + * Create an iterator capable of walking a JSON document. + * + * The document must have already passed through stage 1. + */ + simdjson_inline json_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index); - /** - * Look at the next token. - * - * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). - * - * They may include invalid JSON as well (such as `1.2.3` or `ture`). - */ - simdjson_inline const uint8_t* peek() const noexcept; - /** - * Advance to the next token. - * - * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). - * - * They may include invalid JSON as well (such as `1.2.3` or `ture`). - */ - simdjson_inline const uint8_t* advance() noexcept; - /** - * Get the remaining length of the document, from the start of the current token. - */ - simdjson_inline size_t remaining_len() const noexcept; - /** - * Check if we are at the end of the document. - * - * If this is true, there are no more tokens. - */ - simdjson_inline bool at_eof() const noexcept; - /** - * Check if we are at the beginning of the document. - */ - simdjson_inline bool at_beginning() const noexcept; - simdjson_inline uint8_t last_structural() const noexcept; + /** + * Look at the next token. + * + * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). + * + * They may include invalid JSON as well (such as `1.2.3` or `ture`). + */ + simdjson_inline const uint8_t *peek() const noexcept; + /** + * Advance to the next token. + * + * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). + * + * They may include invalid JSON as well (such as `1.2.3` or `ture`). + */ + simdjson_inline const uint8_t *advance() noexcept; + /** + * Get the remaining length of the document, from the start of the current token. + */ + simdjson_inline size_t remaining_len() const noexcept; + /** + * Check if we are at the end of the document. + * + * If this is true, there are no more tokens. + */ + simdjson_inline bool at_eof() const noexcept; + /** + * Check if we are at the beginning of the document. + */ + simdjson_inline bool at_beginning() const noexcept; + simdjson_inline uint8_t last_structural() const noexcept; - /** - * Log that a value has been found. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_value(const char* type) const noexcept; - /** - * Log the start of a multipart value. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_start_value(const char* type) const noexcept; - /** - * Log the end of a multipart value. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_end_value(const char* type) const noexcept; - /** - * Log an error. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_error(const char* error) const noexcept; + /** + * Log that a value has been found. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_value(const char *type) const noexcept; + /** + * Log the start of a multipart value. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_start_value(const char *type) const noexcept; + /** + * Log the end of a multipart value. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_end_value(const char *type) const noexcept; + /** + * Log an error. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_error(const char *error) const noexcept; - template - simdjson_warn_unused simdjson_inline error_code visit_root_primitive(V& visitor, const uint8_t* value) noexcept; - template - simdjson_warn_unused simdjson_inline error_code visit_primitive(V& visitor, const uint8_t* value) noexcept; - }; + template + simdjson_warn_unused simdjson_inline error_code visit_root_primitive(V &visitor, const uint8_t *value) noexcept; + template + simdjson_warn_unused simdjson_inline error_code visit_primitive(V &visitor, const uint8_t *value) noexcept; +}; - template - simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V& visitor) noexcept { - logger::log_start(); +template +simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &visitor) noexcept { + logger::log_start(); - // - // Start the document - // - if (at_eof()) { return EMPTY; } - log_start_value("document"); - SIMDJSON_TRY(visitor.visit_document_start(*this)); + // + // Start the document + // + if (at_eof()) { return EMPTY; } + log_start_value("document"); + SIMDJSON_TRY( visitor.visit_document_start(*this) ); - // - // Read first value - // - { - auto value = advance(); + // + // Read first value + // + { + auto value = advance(); - // Make sure the outer object or array is closed before continuing; otherwise, there are ways we - // could get into memory corruption. See https://github.com/simdjson/simdjson/issues/906 - if (!STREAMING) { - switch (*value) { - case '{': if (last_structural() != '}') { log_value("starting brace unmatched"); return TAPE_ERROR; }; break; - case '[': if (last_structural() != ']') { log_value("starting bracket unmatched"); return TAPE_ERROR; }; break; - } - } + // Make sure the outer object or array is closed before continuing; otherwise, there are ways we + // could get into memory corruption. See https://github.com/simdjson/simdjson/issues/906 + if (!STREAMING) { + switch (*value) { + case '{': if (last_structural() != '}') { log_value("starting brace unmatched"); return TAPE_ERROR; }; break; + case '[': if (last_structural() != ']') { log_value("starting bracket unmatched"); return TAPE_ERROR; }; break; + } + } - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY(visitor.visit_empty_object(*this)); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY(visitor.visit_empty_array(*this)); break; } goto array_begin; - default: SIMDJSON_TRY(visitor.visit_root_primitive(*this, value)); break; - } - } - goto document_end; + switch (*value) { + case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; + case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; + default: SIMDJSON_TRY( visitor.visit_root_primitive(*this, value) ); break; + } + } + goto document_end; - // - // Object parser states - // - object_begin: - log_start_value("object"); - depth++; - if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } - dom_parser.is_array[depth] = false; - SIMDJSON_TRY(visitor.visit_object_start(*this)); +// +// Object parser states +// +object_begin: + log_start_value("object"); + depth++; + if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } + dom_parser.is_array[depth] = false; + SIMDJSON_TRY( visitor.visit_object_start(*this) ); - { - auto key = advance(); - if (*key != '"') { log_error("Object does not start with a key"); return TAPE_ERROR; } - SIMDJSON_TRY(visitor.increment_count(*this)); - SIMDJSON_TRY(visitor.visit_key(*this, key)); - } + { + auto key = advance(); + if (*key != '"') { log_error("Object does not start with a key"); return TAPE_ERROR; } + SIMDJSON_TRY( visitor.increment_count(*this) ); + SIMDJSON_TRY( visitor.visit_key(*this, key) ); + } - object_field: - if (simdjson_unlikely(*advance() != ':')) { log_error("Missing colon after key in object"); return TAPE_ERROR; } - { - auto value = advance(); - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY(visitor.visit_empty_object(*this)); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY(visitor.visit_empty_array(*this)); break; } goto array_begin; - default: SIMDJSON_TRY(visitor.visit_primitive(*this, value)); break; - } - } +object_field: + if (simdjson_unlikely( *advance() != ':' )) { log_error("Missing colon after key in object"); return TAPE_ERROR; } + { + auto value = advance(); + switch (*value) { + case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; + case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; + default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break; + } + } - object_continue: - switch (*advance()) { - case ',': - SIMDJSON_TRY(visitor.increment_count(*this)); - { - auto key = advance(); - if (simdjson_unlikely(*key != '"')) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; } - SIMDJSON_TRY(visitor.visit_key(*this, key)); - } - goto object_field; - case '}': log_end_value("object"); SIMDJSON_TRY(visitor.visit_object_end(*this)); goto scope_end; - default: log_error("No comma between object fields"); return TAPE_ERROR; - } +object_continue: + switch (*advance()) { + case ',': + SIMDJSON_TRY( visitor.increment_count(*this) ); + { + auto key = advance(); + if (simdjson_unlikely( *key != '"' )) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; } + SIMDJSON_TRY( visitor.visit_key(*this, key) ); + } + goto object_field; + case '}': log_end_value("object"); SIMDJSON_TRY( visitor.visit_object_end(*this) ); goto scope_end; + default: log_error("No comma between object fields"); return TAPE_ERROR; + } - scope_end: - depth--; - if (depth == 0) { goto document_end; } - if (dom_parser.is_array[depth]) { goto array_continue; } - goto object_continue; +scope_end: + depth--; + if (depth == 0) { goto document_end; } + if (dom_parser.is_array[depth]) { goto array_continue; } + goto object_continue; - // - // Array parser states - // - array_begin: - log_start_value("array"); - depth++; - if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } - dom_parser.is_array[depth] = true; - SIMDJSON_TRY(visitor.visit_array_start(*this)); - SIMDJSON_TRY(visitor.increment_count(*this)); +// +// Array parser states +// +array_begin: + log_start_value("array"); + depth++; + if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } + dom_parser.is_array[depth] = true; + SIMDJSON_TRY( visitor.visit_array_start(*this) ); + SIMDJSON_TRY( visitor.increment_count(*this) ); - array_value: - { - auto value = advance(); - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY(visitor.visit_empty_object(*this)); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY(visitor.visit_empty_array(*this)); break; } goto array_begin; - default: SIMDJSON_TRY(visitor.visit_primitive(*this, value)); break; - } - } +array_value: + { + auto value = advance(); + switch (*value) { + case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; + case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; + default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break; + } + } - array_continue: - switch (*advance()) { - case ',': SIMDJSON_TRY(visitor.increment_count(*this)); goto array_value; - case ']': log_end_value("array"); SIMDJSON_TRY(visitor.visit_array_end(*this)); goto scope_end; - default: log_error("Missing comma between array values"); return TAPE_ERROR; - } +array_continue: + switch (*advance()) { + case ',': SIMDJSON_TRY( visitor.increment_count(*this) ); goto array_value; + case ']': log_end_value("array"); SIMDJSON_TRY( visitor.visit_array_end(*this) ); goto scope_end; + default: log_error("Missing comma between array values"); return TAPE_ERROR; + } - document_end: - log_end_value("document"); - SIMDJSON_TRY(visitor.visit_document_end(*this)); +document_end: + log_end_value("document"); + SIMDJSON_TRY( visitor.visit_document_end(*this) ); - dom_parser.next_structural_index = uint32_t(next_structural - &dom_parser.structural_indexes[0]); + dom_parser.next_structural_index = uint32_t(next_structural - &dom_parser.structural_indexes[0]); - // If we didn't make it to the end, it's an error - if (!STREAMING && dom_parser.next_structural_index != dom_parser.n_structural_indexes) { - log_error("More than one JSON value at the root of the document, or extra characters at the end of the JSON!"); - return TAPE_ERROR; - } + // If we didn't make it to the end, it's an error + if ( !STREAMING && dom_parser.next_structural_index != dom_parser.n_structural_indexes ) { + log_error("More than one JSON value at the root of the document, or extra characters at the end of the JSON!"); + return TAPE_ERROR; + } - return SUCCESS; + return SUCCESS; - } // walk_document() +} // walk_document() - simdjson_inline json_iterator::json_iterator(dom_parser_implementation& _dom_parser, size_t start_structural_index) - : buf{ _dom_parser.buf }, - next_structural{ &_dom_parser.structural_indexes[start_structural_index] }, - dom_parser{ _dom_parser } { - } +simdjson_inline json_iterator::json_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index) + : buf{_dom_parser.buf}, + next_structural{&_dom_parser.structural_indexes[start_structural_index]}, + dom_parser{_dom_parser} { +} - simdjson_inline const uint8_t* json_iterator::peek() const noexcept { - return &buf[*(next_structural)]; - } - simdjson_inline const uint8_t* json_iterator::advance() noexcept { - return &buf[*(next_structural++)]; - } - simdjson_inline size_t json_iterator::remaining_len() const noexcept { - return dom_parser.len - *(next_structural - 1); - } +simdjson_inline const uint8_t *json_iterator::peek() const noexcept { + return &buf[*(next_structural)]; +} +simdjson_inline const uint8_t *json_iterator::advance() noexcept { + return &buf[*(next_structural++)]; +} +simdjson_inline size_t json_iterator::remaining_len() const noexcept { + return dom_parser.len - *(next_structural-1); +} - simdjson_inline bool json_iterator::at_eof() const noexcept { - return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; - } - simdjson_inline bool json_iterator::at_beginning() const noexcept { - return next_structural == dom_parser.structural_indexes.get(); - } - simdjson_inline uint8_t json_iterator::last_structural() const noexcept { - return buf[dom_parser.structural_indexes[dom_parser.n_structural_indexes - 1]]; - } +simdjson_inline bool json_iterator::at_eof() const noexcept { + return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; +} +simdjson_inline bool json_iterator::at_beginning() const noexcept { + return next_structural == dom_parser.structural_indexes.get(); +} +simdjson_inline uint8_t json_iterator::last_structural() const noexcept { + return buf[dom_parser.structural_indexes[dom_parser.n_structural_indexes - 1]]; +} - simdjson_inline void json_iterator::log_value(const char* type) const noexcept { - logger::log_line(*this, "", type, ""); - } +simdjson_inline void json_iterator::log_value(const char *type) const noexcept { + logger::log_line(*this, "", type, ""); +} - simdjson_inline void json_iterator::log_start_value(const char* type) const noexcept { - logger::log_line(*this, "+", type, ""); - if (logger::LOG_ENABLED) { logger::log_depth++; } - } +simdjson_inline void json_iterator::log_start_value(const char *type) const noexcept { + logger::log_line(*this, "+", type, ""); + if (logger::LOG_ENABLED) { logger::log_depth++; } +} - simdjson_inline void json_iterator::log_end_value(const char* type) const noexcept { - if (logger::LOG_ENABLED) { logger::log_depth--; } - logger::log_line(*this, "-", type, ""); - } +simdjson_inline void json_iterator::log_end_value(const char *type) const noexcept { + if (logger::LOG_ENABLED) { logger::log_depth--; } + logger::log_line(*this, "-", type, ""); +} - simdjson_inline void json_iterator::log_error(const char* error) const noexcept { - logger::log_line(*this, "", "ERROR", error); - } +simdjson_inline void json_iterator::log_error(const char *error) const noexcept { + logger::log_line(*this, "", "ERROR", error); +} - template - simdjson_warn_unused simdjson_inline error_code json_iterator::visit_root_primitive(V& visitor, const uint8_t* value) noexcept { - switch (*value) { - case '"': return visitor.visit_root_string(*this, value); - case 't': return visitor.visit_root_true_atom(*this, value); - case 'f': return visitor.visit_root_false_atom(*this, value); - case 'n': return visitor.visit_root_null_atom(*this, value); - case '-': - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - return visitor.visit_root_number(*this, value); - default: - log_error("Document starts with a non-value character"); - return TAPE_ERROR; - } - } - template - simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V& visitor, const uint8_t* value) noexcept { - // Use the fact that most scalars are going to be either strings or numbers. - if (*value == '"') { - return visitor.visit_string(*this, value); - } - else if (((*value - '0') < 10) || (*value == '-')) { - return visitor.visit_number(*this, value); - } - // true, false, null are uncommon. - switch (*value) { - case 't': return visitor.visit_true_atom(*this, value); - case 'f': return visitor.visit_false_atom(*this, value); - case 'n': return visitor.visit_null_atom(*this, value); - default: - log_error("Non-value found when value was expected!"); - return TAPE_ERROR; - } - } +template +simdjson_warn_unused simdjson_inline error_code json_iterator::visit_root_primitive(V &visitor, const uint8_t *value) noexcept { + switch (*value) { + case '"': return visitor.visit_root_string(*this, value); + case 't': return visitor.visit_root_true_atom(*this, value); + case 'f': return visitor.visit_root_false_atom(*this, value); + case 'n': return visitor.visit_root_null_atom(*this, value); + case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return visitor.visit_root_number(*this, value); + default: + log_error("Document starts with a non-value character"); + return TAPE_ERROR; + } +} +template +simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V &visitor, const uint8_t *value) noexcept { + // Use the fact that most scalars are going to be either strings or numbers. + if(*value == '"') { + return visitor.visit_string(*this, value); + } else if (((*value - '0') < 10) || (*value == '-')) { + return visitor.visit_number(*this, value); + } + // true, false, null are uncommon. + switch (*value) { + case 't': return visitor.visit_true_atom(*this, value); + case 'f': return visitor.visit_false_atom(*this, value); + case 'n': return visitor.visit_null_atom(*this, value); + default: + log_error("Non-value found when value was expected!"); + return TAPE_ERROR; + } +} - } // namespace stage2 - } // unnamed namespace - } // namespace lasx +} // namespace stage2 +} // unnamed namespace +} // namespace rvv_vls } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_JSON_ITERATOR_H -/* end file generic/stage2/json_iterator.h for lasx */ -/* including generic/stage2/stringparsing.h for lasx: #include */ -/* begin file generic/stage2/stringparsing.h for lasx */ +/* end file generic/stage2/json_iterator.h for rvv_vls */ +/* including generic/stage2/stringparsing.h for rvv_vls: #include */ +/* begin file generic/stage2/stringparsing.h for rvv_vls */ +#include #ifndef SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -51544,248 +60121,244 @@ namespace simdjson { // It is intended to be included multiple times and compiled multiple times namespace simdjson { - namespace lasx { - namespace { - /// @private - namespace stringparsing { +namespace rvv_vls { +namespace { +/// @private +namespace stringparsing { - // begin copypasta - // These chars yield themselves: " \ / - // b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab - // u not handled in this table as it's complex - static const uint8_t escape_map[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// begin copypasta +// These chars yield themselves: " \ / +// b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab +// u not handled in this table as it's complex +static const uint8_t escape_map[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0. + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5. - 0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6. - 0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7. + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4. + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5. + 0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6. + 0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - }; + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; - // handle a unicode codepoint - // write appropriate values into dest - // src will advance 6 bytes or 12 bytes - // dest will advance a variable amount (return via pointer) - // return true if the unicode codepoint was valid - // We work in little-endian then swap at write time - simdjson_warn_unused - simdjson_inline bool handle_unicode_codepoint(const uint8_t** src_ptr, - uint8_t** dst_ptr, bool allow_replacement) { - // Use the default Unicode Character 'REPLACEMENT CHARACTER' (U+FFFD) - constexpr uint32_t substitution_code_point = 0xfffd; - // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the - // conversion is not valid; we defer the check for this to inside the - // multilingual plane check. - uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); - *src_ptr += 6; +// handle a unicode codepoint +// write appropriate values into dest +// src will advance 6 bytes or 12 bytes +// dest will advance a variable amount (return via pointer) +// return true if the unicode codepoint was valid +// We work in little-endian then swap at write time +simdjson_warn_unused +simdjson_inline bool handle_unicode_codepoint(const uint8_t **src_ptr, + uint8_t **dst_ptr, bool allow_replacement) { + // Use the default Unicode Character 'REPLACEMENT CHARACTER' (U+FFFD) + constexpr uint32_t substitution_code_point = 0xfffd; + // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the + // conversion is not valid; we defer the check for this to inside the + // multilingual plane check. + uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); + *src_ptr += 6; - // If we found a high surrogate, we must - // check for low surrogate for characters - // outside the Basic - // Multilingual Plane. - if (code_point >= 0xd800 && code_point < 0xdc00) { - const uint8_t* src_data = *src_ptr; - /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ - if (((src_data[0] << 8) | src_data[1]) != ((static_cast ('\\') << 8) | static_cast ('u'))) { - if (!allow_replacement) { return false; } - code_point = substitution_code_point; - } - else { - uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); + // If we found a high surrogate, we must + // check for low surrogate for characters + // outside the Basic + // Multilingual Plane. + if (code_point >= 0xd800 && code_point < 0xdc00) { + const uint8_t *src_data = *src_ptr; + /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ + if (((src_data[0] << 8) | src_data[1]) != ((static_cast ('\\') << 8) | static_cast ('u'))) { + if(!allow_replacement) { return false; } + code_point = substitution_code_point; + } else { + uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); - // We have already checked that the high surrogate is valid and - // (code_point - 0xd800) < 1024. - // - // Check that code_point_2 is in the range 0xdc00..0xdfff - // and that code_point_2 was parsed from valid hex. - uint32_t low_bit = code_point_2 - 0xdc00; - if (low_bit >> 10) { - if (!allow_replacement) { return false; } - code_point = substitution_code_point; - } - else { - code_point = (((code_point - 0xd800) << 10) | low_bit) + 0x10000; - *src_ptr += 6; - } + // We have already checked that the high surrogate is valid and + // (code_point - 0xd800) < 1024. + // + // Check that code_point_2 is in the range 0xdc00..0xdfff + // and that code_point_2 was parsed from valid hex. + uint32_t low_bit = code_point_2 - 0xdc00; + if (low_bit >> 10) { + if(!allow_replacement) { return false; } + code_point = substitution_code_point; + } else { + code_point = (((code_point - 0xd800) << 10) | low_bit) + 0x10000; + *src_ptr += 6; + } - } - } - else if (code_point >= 0xdc00 && code_point <= 0xdfff) { - // If we encounter a low surrogate (not preceded by a high surrogate) - // then we have an error. - if (!allow_replacement) { return false; } - code_point = substitution_code_point; - } - size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); - *dst_ptr += offset; - return offset > 0; - } + } + } else if (code_point >= 0xdc00 && code_point <= 0xdfff) { + // If we encounter a low surrogate (not preceded by a high surrogate) + // then we have an error. + if(!allow_replacement) { return false; } + code_point = substitution_code_point; + } + size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); + *dst_ptr += offset; + return offset > 0; +} - // handle a unicode codepoint using the wobbly convention - // https://simonsapin.github.io/wtf-8/ - // write appropriate values into dest - // src will advance 6 bytes or 12 bytes - // dest will advance a variable amount (return via pointer) - // return true if the unicode codepoint was valid - // We work in little-endian then swap at write time - simdjson_warn_unused - simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t** src_ptr, - uint8_t** dst_ptr) { - // It is not ideal that this function is nearly identical to handle_unicode_codepoint. - // - // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the - // conversion is not valid; we defer the check for this to inside the - // multilingual plane check. - uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); - *src_ptr += 6; - // If we found a high surrogate, we must - // check for low surrogate for characters - // outside the Basic - // Multilingual Plane. - if (code_point >= 0xd800 && code_point < 0xdc00) { - const uint8_t* src_data = *src_ptr; - /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ - if (((src_data[0] << 8) | src_data[1]) == ((static_cast ('\\') << 8) | static_cast ('u'))) { - uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); - uint32_t low_bit = code_point_2 - 0xdc00; - if ((low_bit >> 10) == 0) { - code_point = - (((code_point - 0xd800) << 10) | low_bit) + 0x10000; - *src_ptr += 6; - } - } - } +// handle a unicode codepoint using the wobbly convention +// https://simonsapin.github.io/wtf-8/ +// write appropriate values into dest +// src will advance 6 bytes or 12 bytes +// dest will advance a variable amount (return via pointer) +// return true if the unicode codepoint was valid +// We work in little-endian then swap at write time +simdjson_warn_unused +simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t **src_ptr, + uint8_t **dst_ptr) { + // It is not ideal that this function is nearly identical to handle_unicode_codepoint. + // + // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the + // conversion is not valid; we defer the check for this to inside the + // multilingual plane check. + uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); + *src_ptr += 6; + // If we found a high surrogate, we must + // check for low surrogate for characters + // outside the Basic + // Multilingual Plane. + if (code_point >= 0xd800 && code_point < 0xdc00) { + const uint8_t *src_data = *src_ptr; + /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ + if (((src_data[0] << 8) | src_data[1]) == ((static_cast ('\\') << 8) | static_cast ('u'))) { + uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); + uint32_t low_bit = code_point_2 - 0xdc00; + if ((low_bit >> 10) == 0) { + code_point = + (((code_point - 0xd800) << 10) | low_bit) + 0x10000; + *src_ptr += 6; + } + } + } - size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); - *dst_ptr += offset; - return offset > 0; - } + size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); + *dst_ptr += offset; + return offset > 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. - */ - simdjson_warn_unused simdjson_inline uint8_t* parse_string(const uint8_t* src, uint8_t* dst, bool allow_replacement) { - while (1) { - // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); - // If the next thing is the end quote, copy and return - if (bs_quote.has_quote_first()) { - // we encountered quotes first. Move dst to point to quotes and exit - return dst + bs_quote.quote_index(); - } - if (bs_quote.has_backslash()) { - /* find out where the backspace is */ - auto bs_dist = bs_quote.backslash_index(); - uint8_t escape_char = src[bs_dist + 1]; - /* we encountered backslash first. Handle backslash */ - if (escape_char == 'u') { - /* move src/dst up to the start; they will be further adjusted - within the unicode codepoint handling code. */ - src += bs_dist; - dst += bs_dist; - if (!handle_unicode_codepoint(&src, &dst, allow_replacement)) { - return nullptr; - } - } - else { - /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and - * write bs_dist+1 characters to output - * note this may reach beyond the part of the buffer we've actually - * seen. I think this is ok */ - uint8_t escape_result = escape_map[escape_char]; - if (escape_result == 0u) { - return nullptr; /* bogus escape value is an error */ - } - dst[bs_dist] = escape_result; - src += bs_dist + 2; - dst += bs_dist + 1; - } - } - else { - /* they are the same. Since they can't co-occur, it means we - * encountered neither. */ - src += backslash_and_quote::BYTES_PROCESSED; - dst += backslash_and_quote::BYTES_PROCESSED; - } - } - } +/** + * 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. + */ +simdjson_warn_unused simdjson_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) { + while (1) { + // Copy the next n bytes, and find the backslash and quote in them. + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); + // If the next thing is the end quote, copy and return + if (bs_quote.has_quote_first()) { + // we encountered quotes first. Move dst to point to quotes and exit + return dst + bs_quote.quote_index(); + } + if (bs_quote.has_backslash()) { + /* find out where the backspace is */ + auto bs_dist = bs_quote.backslash_index(); + uint8_t escape_char = src[bs_dist + 1]; + /* we encountered backslash first. Handle backslash */ + if (escape_char == 'u') { + /* move src/dst up to the start; they will be further adjusted + within the unicode codepoint handling code. */ + src += bs_dist; + dst += bs_dist; + if (!handle_unicode_codepoint(&src, &dst, allow_replacement)) { + return nullptr; + } + } else { + /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and + * write bs_dist+1 characters to output + * note this may reach beyond the part of the buffer we've actually + * seen. I think this is ok */ + uint8_t escape_result = escape_map[escape_char]; + if (escape_result == 0u) { + return nullptr; /* bogus escape value is an error */ + } + dst[bs_dist] = escape_result; + src += bs_dist + 2; + dst += bs_dist + 1; + } + } else { + /* they are the same. Since they can't co-occur, it means we + * encountered neither. */ + src += backslash_and_quote::BYTES_PROCESSED; + dst += backslash_and_quote::BYTES_PROCESSED; + } + } +} - simdjson_warn_unused simdjson_inline uint8_t* parse_wobbly_string(const uint8_t* src, uint8_t* dst) { - // It is not ideal that this function is nearly identical to parse_string. - while (1) { - // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); - // If the next thing is the end quote, copy and return - if (bs_quote.has_quote_first()) { - // we encountered quotes first. Move dst to point to quotes and exit - return dst + bs_quote.quote_index(); - } - if (bs_quote.has_backslash()) { - /* find out where the backspace is */ - auto bs_dist = bs_quote.backslash_index(); - uint8_t escape_char = src[bs_dist + 1]; - /* we encountered backslash first. Handle backslash */ - if (escape_char == 'u') { - /* move src/dst up to the start; they will be further adjusted - within the unicode codepoint handling code. */ - src += bs_dist; - dst += bs_dist; - if (!handle_unicode_codepoint_wobbly(&src, &dst)) { - return nullptr; - } - } - else { - /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and - * write bs_dist+1 characters to output - * note this may reach beyond the part of the buffer we've actually - * seen. I think this is ok */ - uint8_t escape_result = escape_map[escape_char]; - if (escape_result == 0u) { - return nullptr; /* bogus escape value is an error */ - } - dst[bs_dist] = escape_result; - src += bs_dist + 2; - dst += bs_dist + 1; - } - } - else { - /* they are the same. Since they can't co-occur, it means we - * encountered neither. */ - src += backslash_and_quote::BYTES_PROCESSED; - dst += backslash_and_quote::BYTES_PROCESSED; - } - } - } +simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) { + // It is not ideal that this function is nearly identical to parse_string. + while (1) { + // Copy the next n bytes, and find the backslash and quote in them. + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); + // If the next thing is the end quote, copy and return + if (bs_quote.has_quote_first()) { + // we encountered quotes first. Move dst to point to quotes and exit + return dst + bs_quote.quote_index(); + } + if (bs_quote.has_backslash()) { + /* find out where the backspace is */ + auto bs_dist = bs_quote.backslash_index(); + uint8_t escape_char = src[bs_dist + 1]; + /* we encountered backslash first. Handle backslash */ + if (escape_char == 'u') { + /* move src/dst up to the start; they will be further adjusted + within the unicode codepoint handling code. */ + src += bs_dist; + dst += bs_dist; + if (!handle_unicode_codepoint_wobbly(&src, &dst)) { + return nullptr; + } + } else { + /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and + * write bs_dist+1 characters to output + * note this may reach beyond the part of the buffer we've actually + * seen. I think this is ok */ + uint8_t escape_result = escape_map[escape_char]; + if (escape_result == 0u) { + return nullptr; /* bogus escape value is an error */ + } + dst[bs_dist] = escape_result; + src += bs_dist + 2; + dst += bs_dist + 1; + } + } else { + /* they are the same. Since they can't co-occur, it means we + * encountered neither. */ + src += backslash_and_quote::BYTES_PROCESSED; + dst += backslash_and_quote::BYTES_PROCESSED; + } + } +} - } // namespace stringparsing - } // unnamed namespace - } // namespace lasx +} // namespace stringparsing + +} // unnamed namespace +} // namespace rvv_vls } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H -/* end file generic/stage2/stringparsing.h for lasx */ -/* including generic/stage2/structural_iterator.h for lasx: #include */ -/* begin file generic/stage2/structural_iterator.h for lasx */ +/* end file generic/stage2/stringparsing.h for rvv_vls */ +/* including generic/stage2/structural_iterator.h for rvv_vls: #include */ +/* begin file generic/stage2/structural_iterator.h for rvv_vls */ #ifndef SIMDJSON_SRC_GENERIC_STAGE2_STRUCTURAL_ITERATOR_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -51795,64 +60368,64 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace { - namespace stage2 { +namespace rvv_vls { +namespace { +namespace stage2 { - class structural_iterator { - public: - const uint8_t* const buf; - uint32_t* next_structural; - dom_parser_implementation& dom_parser; +class structural_iterator { +public: + const uint8_t* const buf; + uint32_t *next_structural; + dom_parser_implementation &dom_parser; - // Start a structural - simdjson_inline structural_iterator(dom_parser_implementation& _dom_parser, size_t start_structural_index) - : buf{ _dom_parser.buf }, - next_structural{ &_dom_parser.structural_indexes[start_structural_index] }, - dom_parser{ _dom_parser } { - } - // Get the buffer position of the current structural character - simdjson_inline const uint8_t* current() { - return &buf[*(next_structural - 1)]; - } - // Get the current structural character - simdjson_inline char current_char() { - return buf[*(next_structural - 1)]; - } - // Get the next structural character without advancing - simdjson_inline char peek_next_char() { - return buf[*next_structural]; - } - simdjson_inline const uint8_t* peek() { - return &buf[*next_structural]; - } - simdjson_inline const uint8_t* advance() { - return &buf[*(next_structural++)]; - } - simdjson_inline char advance_char() { - return buf[*(next_structural++)]; - } - simdjson_inline size_t remaining_len() { - return dom_parser.len - *(next_structural - 1); - } + // Start a structural + simdjson_inline structural_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index) + : buf{_dom_parser.buf}, + next_structural{&_dom_parser.structural_indexes[start_structural_index]}, + dom_parser{_dom_parser} { + } + // Get the buffer position of the current structural character + simdjson_inline const uint8_t* current() { + return &buf[*(next_structural-1)]; + } + // Get the current structural character + simdjson_inline char current_char() { + return buf[*(next_structural-1)]; + } + // Get the next structural character without advancing + simdjson_inline char peek_next_char() { + return buf[*next_structural]; + } + simdjson_inline const uint8_t* peek() { + return &buf[*next_structural]; + } + simdjson_inline const uint8_t* advance() { + return &buf[*(next_structural++)]; + } + simdjson_inline char advance_char() { + return buf[*(next_structural++)]; + } + simdjson_inline size_t remaining_len() { + return dom_parser.len - *(next_structural-1); + } - simdjson_inline bool at_end() { - return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; - } - simdjson_inline bool at_beginning() { - return next_structural == dom_parser.structural_indexes.get(); - } - }; + simdjson_inline bool at_end() { + return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; + } + simdjson_inline bool at_beginning() { + return next_structural == dom_parser.structural_indexes.get(); + } +}; - } // namespace stage2 - } // unnamed namespace - } // namespace lasx +} // namespace stage2 +} // unnamed namespace +} // namespace rvv_vls } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_STRUCTURAL_ITERATOR_H -/* end file generic/stage2/structural_iterator.h for lasx */ -/* including generic/stage2/tape_builder.h for lasx: #include */ -/* begin file generic/stage2/tape_builder.h for lasx */ +/* end file generic/stage2/structural_iterator.h for rvv_vls */ +/* including generic/stage2/tape_builder.h for rvv_vls: #include */ +/* begin file generic/stage2/tape_builder.h for rvv_vls */ #ifndef SIMDJSON_SRC_GENERIC_STAGE2_TAPE_BUILDER_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -51869,353 +60442,351 @@ namespace simdjson { namespace simdjson { - namespace lasx { - namespace { - namespace stage2 { +namespace rvv_vls { +namespace { +namespace stage2 { - struct tape_builder { - template - simdjson_warn_unused static simdjson_inline error_code parse_document( - dom_parser_implementation& dom_parser, - dom::document& doc) noexcept; +struct tape_builder { + template + simdjson_warn_unused static simdjson_inline error_code parse_document( + dom_parser_implementation &dom_parser, + dom::document &doc) noexcept; - /** Called when a non-empty document starts. */ - simdjson_warn_unused simdjson_inline error_code visit_document_start(json_iterator& iter) noexcept; - /** Called when a non-empty document ends without error. */ - simdjson_warn_unused simdjson_inline error_code visit_document_end(json_iterator& iter) noexcept; + /** Called when a non-empty document starts. */ + simdjson_warn_unused simdjson_inline error_code visit_document_start(json_iterator &iter) noexcept; + /** Called when a non-empty document ends without error. */ + simdjson_warn_unused simdjson_inline error_code visit_document_end(json_iterator &iter) noexcept; - /** Called when a non-empty array starts. */ - simdjson_warn_unused simdjson_inline error_code visit_array_start(json_iterator& iter) noexcept; - /** Called when a non-empty array ends. */ - simdjson_warn_unused simdjson_inline error_code visit_array_end(json_iterator& iter) noexcept; - /** Called when an empty array is found. */ - simdjson_warn_unused simdjson_inline error_code visit_empty_array(json_iterator& iter) noexcept; + /** Called when a non-empty array starts. */ + simdjson_warn_unused simdjson_inline error_code visit_array_start(json_iterator &iter) noexcept; + /** Called when a non-empty array ends. */ + simdjson_warn_unused simdjson_inline error_code visit_array_end(json_iterator &iter) noexcept; + /** Called when an empty array is found. */ + simdjson_warn_unused simdjson_inline error_code visit_empty_array(json_iterator &iter) noexcept; - /** Called when a non-empty object starts. */ - simdjson_warn_unused simdjson_inline error_code visit_object_start(json_iterator& iter) noexcept; - /** - * Called when a key in a field is encountered. - * - * primitive, visit_object_start, visit_empty_object, visit_array_start, or visit_empty_array - * will be called after this with the field value. - */ - simdjson_warn_unused simdjson_inline error_code visit_key(json_iterator& iter, const uint8_t* key) noexcept; - /** Called when a non-empty object ends. */ - simdjson_warn_unused simdjson_inline error_code visit_object_end(json_iterator& iter) noexcept; - /** Called when an empty object is found. */ - simdjson_warn_unused simdjson_inline error_code visit_empty_object(json_iterator& iter) noexcept; + /** Called when a non-empty object starts. */ + simdjson_warn_unused simdjson_inline error_code visit_object_start(json_iterator &iter) noexcept; + /** + * Called when a key in a field is encountered. + * + * primitive, visit_object_start, visit_empty_object, visit_array_start, or visit_empty_array + * will be called after this with the field value. + */ + simdjson_warn_unused simdjson_inline error_code visit_key(json_iterator &iter, const uint8_t *key) noexcept; + /** Called when a non-empty object ends. */ + simdjson_warn_unused simdjson_inline error_code visit_object_end(json_iterator &iter) noexcept; + /** Called when an empty object is found. */ + simdjson_warn_unused simdjson_inline error_code visit_empty_object(json_iterator &iter) noexcept; - /** - * Called when a string, number, boolean or null is found. - */ - simdjson_warn_unused simdjson_inline error_code visit_primitive(json_iterator& iter, const uint8_t* value) noexcept; - /** - * Called when a string, number, boolean or null is found at the top level of a document (i.e. - * when there is no array or object and the entire document is a single string, number, boolean or - * null. - * - * This is separate from primitive() because simdjson's normal primitive parsing routines assume - * there is at least one more token after the value, which is only true in an array or object. - */ - simdjson_warn_unused simdjson_inline error_code visit_root_primitive(json_iterator& iter, const uint8_t* value) noexcept; + /** + * Called when a string, number, boolean or null is found. + */ + simdjson_warn_unused simdjson_inline error_code visit_primitive(json_iterator &iter, const uint8_t *value) noexcept; + /** + * Called when a string, number, boolean or null is found at the top level of a document (i.e. + * when there is no array or object and the entire document is a single string, number, boolean or + * null. + * + * This is separate from primitive() because simdjson's normal primitive parsing routines assume + * there is at least one more token after the value, which is only true in an array or object. + */ + simdjson_warn_unused simdjson_inline error_code visit_root_primitive(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_string(json_iterator& iter, const uint8_t* value, bool key = false) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_number(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_true_atom(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_false_atom(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_null_atom(json_iterator& iter, const uint8_t* value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_string(json_iterator &iter, const uint8_t *value, bool key = false) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_number(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_true_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_false_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_null_atom(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_string(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_number(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_true_atom(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_false_atom(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_null_atom(json_iterator& iter, const uint8_t* value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_string(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_number(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_true_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept; - /** Called each time a new field or element in an array or object is found. */ - simdjson_warn_unused simdjson_inline error_code increment_count(json_iterator& iter) noexcept; + /** Called each time a new field or element in an array or object is found. */ + simdjson_warn_unused simdjson_inline error_code increment_count(json_iterator &iter) noexcept; - /** Next location to write to tape */ - tape_writer tape; - private: - /** Next write location in the string buf for stage 2 parsing */ - uint8_t* current_string_buf_loc; + /** Next location to write to tape */ + tape_writer tape; +private: + /** Next write location in the string buf for stage 2 parsing */ + uint8_t *current_string_buf_loc; - simdjson_inline tape_builder(dom::document& doc) noexcept; + simdjson_inline tape_builder(dom::document &doc) noexcept; - simdjson_inline uint32_t next_tape_index(json_iterator& iter) const noexcept; - simdjson_inline void start_container(json_iterator& iter) noexcept; - simdjson_warn_unused simdjson_inline error_code end_container(json_iterator& iter, internal::tape_type start, internal::tape_type end) noexcept; - simdjson_warn_unused simdjson_inline error_code empty_container(json_iterator& iter, internal::tape_type start, internal::tape_type end) noexcept; - simdjson_inline uint8_t* on_start_string(json_iterator& iter) noexcept; - simdjson_inline void on_end_string(uint8_t* dst) noexcept; - }; // struct tape_builder + simdjson_inline uint32_t next_tape_index(json_iterator &iter) const noexcept; + simdjson_inline void start_container(json_iterator &iter) noexcept; + simdjson_warn_unused simdjson_inline error_code end_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept; + simdjson_warn_unused simdjson_inline error_code empty_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept; + simdjson_inline uint8_t *on_start_string(json_iterator &iter) noexcept; + simdjson_inline void on_end_string(uint8_t *dst) noexcept; +}; // struct tape_builder - template - simdjson_warn_unused simdjson_inline error_code tape_builder::parse_document( - dom_parser_implementation& dom_parser, - dom::document& doc) noexcept { - dom_parser.doc = &doc; - json_iterator iter(dom_parser, STREAMING ? dom_parser.next_structural_index : 0); - tape_builder builder(doc); - return iter.walk_document(builder); - } +template +simdjson_warn_unused simdjson_inline error_code tape_builder::parse_document( + dom_parser_implementation &dom_parser, + dom::document &doc) noexcept { + dom_parser.doc = &doc; + json_iterator iter(dom_parser, STREAMING ? dom_parser.next_structural_index : 0); + tape_builder builder(doc); + return iter.walk_document(builder); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_primitive(json_iterator& iter, const uint8_t* value) noexcept { - return iter.visit_root_primitive(*this, value); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_primitive(json_iterator& iter, const uint8_t* value) noexcept { - return iter.visit_primitive(*this, value); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_object(json_iterator& iter) noexcept { - return empty_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_array(json_iterator& iter) noexcept { - return empty_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_primitive(json_iterator &iter, const uint8_t *value) noexcept { + return iter.visit_root_primitive(*this, value); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_primitive(json_iterator &iter, const uint8_t *value) noexcept { + return iter.visit_primitive(*this, value); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_object(json_iterator &iter) noexcept { + return empty_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_array(json_iterator &iter) noexcept { + return empty_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_start(json_iterator& iter) noexcept { - start_container(iter); - return SUCCESS; - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_start(json_iterator& iter) noexcept { - start_container(iter); - return SUCCESS; - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_start(json_iterator& iter) noexcept { - start_container(iter); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_start(json_iterator &iter) noexcept { + start_container(iter); + return SUCCESS; +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_start(json_iterator &iter) noexcept { + start_container(iter); + return SUCCESS; +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_start(json_iterator &iter) noexcept { + start_container(iter); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_end(json_iterator& iter) noexcept { - return end_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_end(json_iterator& iter) noexcept { - return end_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_end(json_iterator& iter) noexcept { - constexpr uint32_t start_tape_index = 0; - tape.append(start_tape_index, internal::tape_type::ROOT); - tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter), internal::tape_type::ROOT); - return SUCCESS; - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_key(json_iterator& iter, const uint8_t* key) noexcept { - return visit_string(iter, key, true); - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_end(json_iterator &iter) noexcept { + return end_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_end(json_iterator &iter) noexcept { + return end_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_end(json_iterator &iter) noexcept { + constexpr uint32_t start_tape_index = 0; + tape.append(start_tape_index, internal::tape_type::ROOT); + tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter), internal::tape_type::ROOT); + return SUCCESS; +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_key(json_iterator &iter, const uint8_t *key) noexcept { + return visit_string(iter, key, true); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::increment_count(json_iterator& iter) noexcept { - iter.dom_parser.open_containers[iter.depth].count++; // we have a key value pair in the object at parser.dom_parser.depth - 1 - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::increment_count(json_iterator &iter) noexcept { + iter.dom_parser.open_containers[iter.depth].count++; // we have a key value pair in the object at parser.dom_parser.depth - 1 + return SUCCESS; +} - simdjson_inline tape_builder::tape_builder(dom::document& doc) noexcept : tape{ doc.tape.get() }, current_string_buf_loc{ doc.string_buf.get() } {} +simdjson_inline tape_builder::tape_builder(dom::document &doc) noexcept : tape{doc.tape.get()}, current_string_buf_loc{doc.string_buf.get()} {} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_string(json_iterator& iter, const uint8_t* value, bool key) noexcept { - iter.log_value(key ? "key" : "string"); - uint8_t* dst = on_start_string(iter); - dst = stringparsing::parse_string(value + 1, dst, false); // We do not allow replacement when the escape characters are invalid. - if (dst == nullptr) { - iter.log_error("Invalid escape in string"); - return STRING_ERROR; - } - on_end_string(dst); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_string(json_iterator &iter, const uint8_t *value, bool key) noexcept { + iter.log_value(key ? "key" : "string"); + uint8_t *dst = on_start_string(iter); + dst = stringparsing::parse_string(value+1, dst, false); // We do not allow replacement when the escape characters are invalid. + if (dst == nullptr) { + iter.log_error("Invalid escape in string"); + return STRING_ERROR; + } + on_end_string(dst); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_string(json_iterator& iter, const uint8_t* value) noexcept { - return visit_string(iter, value); - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_string(json_iterator &iter, const uint8_t *value) noexcept { + return visit_string(iter, value); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_number(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("number"); - return numberparsing::parse_number(value, tape); - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_number(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("number"); + return numberparsing::parse_number(value, tape); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_number(json_iterator& iter, const uint8_t* value) noexcept { - // - // We need to make a copy to make sure that the string is space terminated. - // This is not about padding the input, which should already padded up - // to len + SIMDJSON_PADDING. However, we have no control at this stage - // on how the padding was done. What if the input string was padded with nulls? - // It is quite common for an input string to have an extra null character (C string). - // We do not want to allow 9\0 (where \0 is the null character) inside a JSON - // document, but the string "9\0" by itself is fine. So we make a copy and - // pad the input with spaces when we know that there is just one input element. - // This copy is relatively expensive, but it will almost never be called in - // practice unless you are in the strange scenario where you have many JSON - // documents made of single atoms. - // - std::unique_ptrcopy(new (std::nothrow) uint8_t[iter.remaining_len() + SIMDJSON_PADDING]); - if (copy.get() == nullptr) { return MEMALLOC; } - std::memcpy(copy.get(), value, iter.remaining_len()); - std::memset(copy.get() + iter.remaining_len(), ' ', SIMDJSON_PADDING); - error_code error = visit_number(iter, copy.get()); - return error; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_number(json_iterator &iter, const uint8_t *value) noexcept { + // + // We need to make a copy to make sure that the string is space terminated. + // This is not about padding the input, which should already padded up + // to len + SIMDJSON_PADDING. However, we have no control at this stage + // on how the padding was done. What if the input string was padded with nulls? + // It is quite common for an input string to have an extra null character (C string). + // We do not want to allow 9\0 (where \0 is the null character) inside a JSON + // document, but the string "9\0" by itself is fine. So we make a copy and + // pad the input with spaces when we know that there is just one input element. + // This copy is relatively expensive, but it will almost never be called in + // practice unless you are in the strange scenario where you have many JSON + // documents made of single atoms. + // + std::unique_ptrcopy(new (std::nothrow) uint8_t[iter.remaining_len() + SIMDJSON_PADDING]); + if (copy.get() == nullptr) { return MEMALLOC; } + std::memcpy(copy.get(), value, iter.remaining_len()); + std::memset(copy.get() + iter.remaining_len(), ' ', SIMDJSON_PADDING); + error_code error = visit_number(iter, copy.get()); + return error; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_true_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("true"); - if (!atomparsing::is_valid_true_atom(value)) { return T_ATOM_ERROR; } - tape.append(0, internal::tape_type::TRUE_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_true_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("true"); + if (!atomparsing::is_valid_true_atom(value)) { return T_ATOM_ERROR; } + tape.append(0, internal::tape_type::TRUE_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_true_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("true"); - if (!atomparsing::is_valid_true_atom(value, iter.remaining_len())) { return T_ATOM_ERROR; } - tape.append(0, internal::tape_type::TRUE_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_true_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("true"); + if (!atomparsing::is_valid_true_atom(value, iter.remaining_len())) { return T_ATOM_ERROR; } + tape.append(0, internal::tape_type::TRUE_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_false_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("false"); - if (!atomparsing::is_valid_false_atom(value)) { return F_ATOM_ERROR; } - tape.append(0, internal::tape_type::FALSE_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_false_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("false"); + if (!atomparsing::is_valid_false_atom(value)) { return F_ATOM_ERROR; } + tape.append(0, internal::tape_type::FALSE_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_false_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("false"); - if (!atomparsing::is_valid_false_atom(value, iter.remaining_len())) { return F_ATOM_ERROR; } - tape.append(0, internal::tape_type::FALSE_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("false"); + if (!atomparsing::is_valid_false_atom(value, iter.remaining_len())) { return F_ATOM_ERROR; } + tape.append(0, internal::tape_type::FALSE_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_null_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("null"); - if (!atomparsing::is_valid_null_atom(value)) { return N_ATOM_ERROR; } - tape.append(0, internal::tape_type::NULL_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_null_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("null"); + if (!atomparsing::is_valid_null_atom(value)) { return N_ATOM_ERROR; } + tape.append(0, internal::tape_type::NULL_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_null_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("null"); - if (!atomparsing::is_valid_null_atom(value, iter.remaining_len())) { return N_ATOM_ERROR; } - tape.append(0, internal::tape_type::NULL_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("null"); + if (!atomparsing::is_valid_null_atom(value, iter.remaining_len())) { return N_ATOM_ERROR; } + tape.append(0, internal::tape_type::NULL_VALUE); + return SUCCESS; +} - // private: +// private: - simdjson_inline uint32_t tape_builder::next_tape_index(json_iterator& iter) const noexcept { - return uint32_t(tape.next_tape_loc - iter.dom_parser.doc->tape.get()); - } +simdjson_inline uint32_t tape_builder::next_tape_index(json_iterator &iter) const noexcept { + return uint32_t(tape.next_tape_loc - iter.dom_parser.doc->tape.get()); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::empty_container(json_iterator& iter, internal::tape_type start, internal::tape_type end) noexcept { - auto start_index = next_tape_index(iter); - tape.append(start_index + 2, start); - tape.append(start_index, end); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::empty_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept { + auto start_index = next_tape_index(iter); + tape.append(start_index+2, start); + tape.append(start_index, end); + return SUCCESS; +} - simdjson_inline void tape_builder::start_container(json_iterator& iter) noexcept { - iter.dom_parser.open_containers[iter.depth].tape_index = next_tape_index(iter); - iter.dom_parser.open_containers[iter.depth].count = 0; - tape.skip(); // We don't actually *write* the start element until the end. - } +simdjson_inline void tape_builder::start_container(json_iterator &iter) noexcept { + iter.dom_parser.open_containers[iter.depth].tape_index = next_tape_index(iter); + iter.dom_parser.open_containers[iter.depth].count = 0; + tape.skip(); // We don't actually *write* the start element until the end. +} - simdjson_warn_unused simdjson_inline error_code tape_builder::end_container(json_iterator& iter, internal::tape_type start, internal::tape_type end) noexcept { - // Write the ending tape element, pointing at the start location - const uint32_t start_tape_index = iter.dom_parser.open_containers[iter.depth].tape_index; - tape.append(start_tape_index, end); - // Write the start tape element, pointing at the end location (and including count) - // count can overflow if it exceeds 24 bits... so we saturate - // the convention being that a cnt of 0xffffff or more is undetermined in value (>= 0xffffff). - const uint32_t count = iter.dom_parser.open_containers[iter.depth].count; - const uint32_t cntsat = count > 0xFFFFFF ? 0xFFFFFF : count; - tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter) | (uint64_t(cntsat) << 32), start); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::end_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept { + // Write the ending tape element, pointing at the start location + const uint32_t start_tape_index = iter.dom_parser.open_containers[iter.depth].tape_index; + tape.append(start_tape_index, end); + // Write the start tape element, pointing at the end location (and including count) + // count can overflow if it exceeds 24 bits... so we saturate + // the convention being that a cnt of 0xffffff or more is undetermined in value (>= 0xffffff). + const uint32_t count = iter.dom_parser.open_containers[iter.depth].count; + const uint32_t cntsat = count > 0xFFFFFF ? 0xFFFFFF : count; + tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter) | (uint64_t(cntsat) << 32), start); + return SUCCESS; +} - simdjson_inline uint8_t* tape_builder::on_start_string(json_iterator& iter) noexcept { - // we advance the point, accounting for the fact that we have a NULL termination - tape.append(current_string_buf_loc - iter.dom_parser.doc->string_buf.get(), internal::tape_type::STRING); - return current_string_buf_loc + sizeof(uint32_t); - } +simdjson_inline uint8_t *tape_builder::on_start_string(json_iterator &iter) noexcept { + // we advance the point, accounting for the fact that we have a NULL termination + tape.append(current_string_buf_loc - iter.dom_parser.doc->string_buf.get(), internal::tape_type::STRING); + return current_string_buf_loc + sizeof(uint32_t); +} - simdjson_inline void tape_builder::on_end_string(uint8_t* dst) noexcept { - uint32_t str_length = uint32_t(dst - (current_string_buf_loc + sizeof(uint32_t))); - // TODO check for overflow in case someone has a crazy string (>=4GB?) - // But only add the overflow check when the document itself exceeds 4GB - // Currently unneeded because we refuse to parse docs larger or equal to 4GB. - memcpy(current_string_buf_loc, &str_length, sizeof(uint32_t)); - // NULL termination is still handy if you expect all your strings to - // be NULL terminated? It comes at a small cost - *dst = 0; - current_string_buf_loc = dst + 1; - } +simdjson_inline void tape_builder::on_end_string(uint8_t *dst) noexcept { + uint32_t str_length = uint32_t(dst - (current_string_buf_loc + sizeof(uint32_t))); + // TODO check for overflow in case someone has a crazy string (>=4GB?) + // But only add the overflow check when the document itself exceeds 4GB + // Currently unneeded because we refuse to parse docs larger or equal to 4GB. + memcpy(current_string_buf_loc, &str_length, sizeof(uint32_t)); + // NULL termination is still handy if you expect all your strings to + // be NULL terminated? It comes at a small cost + *dst = 0; + current_string_buf_loc = dst + 1; +} - } // namespace stage2 - } // unnamed namespace - } // namespace lasx +} // namespace stage2 +} // unnamed namespace +} // namespace rvv_vls } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_BUILDER_H -/* end file generic/stage2/tape_builder.h for lasx */ -/* end file generic/stage2/amalgamated.h for lasx */ +/* end file generic/stage2/tape_builder.h for rvv_vls */ +/* end file generic/stage2/amalgamated.h for rvv_vls */ // // Stage 1 // namespace simdjson { - namespace lasx { +namespace rvv_vls { - simdjson_warn_unused error_code implementation::create_dom_parser_implementation( - size_t capacity, - size_t max_depth, - std::unique_ptr& dst - ) const noexcept { - dst.reset(new (std::nothrow) dom_parser_implementation()); - if (!dst) { return MEMALLOC; } - if (auto err = dst->set_capacity(capacity)) - return err; - if (auto err = dst->set_max_depth(max_depth)) - return err; - return SUCCESS; - } +simdjson_warn_unused error_code implementation::create_dom_parser_implementation( + size_t capacity, + size_t max_depth, + std::unique_ptr& dst +) const noexcept { + dst.reset( new (std::nothrow) dom_parser_implementation() ); + if (!dst) { return MEMALLOC; } + if (auto err = dst->set_capacity(capacity)) + return err; + if (auto err = dst->set_max_depth(max_depth)) + return err; + return SUCCESS; +} - namespace { +namespace { - using namespace simd; +using namespace simd; - simdjson_inline json_character_block json_character_block::classify(const simd::simd8x64& in) { - // Inspired by haswell. - // LASX use low 5 bits as index. For the 6 operators (:,[]{}), the unique-5bits is [6:2]. - // The ASCII white-space and operators have these values: (char, hex, unique-5bits) - // (' ', 20, 00000) ('\t', 09, 01001) ('\n', 0A, 01010) ('\r', 0D, 01101) - // (',', 2C, 01011) (':', 3A, 01110) ('[', 5B, 10110) ('{', 7B, 11110) (']', 5D, 10111) ('}', 7D, 11111) - const simd8 ws_table = simd8::repeat_16( - ' ', 0, 0, 0, 0, 0, 0, 0, 0, '\t', '\n', 0, 0, '\r', 0, 0 - ); - const simd8 op_table_lo = simd8::repeat_16( - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ',', 0, 0, ':', 0 - ); - const simd8 op_table_hi = simd8::repeat_16( - 0, 0, 0, 0, 0, 0, '[', ']', 0, 0, 0, 0, 0, 0, '{', '}' - ); - uint64_t ws = in.eq({ - in.chunks[0].lookup_16(ws_table), - in.chunks[1].lookup_16(ws_table), - }); - uint64_t op = in.eq({ - __lasx_xvshuf_b(op_table_hi, op_table_lo, in.chunks[0].shr<2>()), - __lasx_xvshuf_b(op_table_hi, op_table_lo, in.chunks[1].shr<2>()), - }); +simdjson_inline json_character_block json_character_block::classify(const simd::simd8x64& in) { + static const uint8_t wsTable[16] = { ' ', 100, 100, 100, 17, 100, 113, 2, 100, '\t', '\n', 112, 100, '\r', 100, 100 }; + static const uint8_t opTable[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ':', '{', ',', '}', 0, 0 }; + vuint8_t vws = __riscv_vle8_v_u8m1(wsTable, 16); + vuint8_t vop = __riscv_vle8_v_u8m1(opTable, 16); + vuint8x64_t lo = __riscv_vand(in, 15, 64); + vuint8x64_t curl = __riscv_vor(in, 0x20, 64); - return { ws, op }; - } +#if __riscv_v_fixed_vlen == 128 + vboolx64_t mws = __riscv_vmseq(simdutf_vrgather_u8m1x4(vws, lo), in, 64); + vboolx64_t mop = __riscv_vmseq(simdutf_vrgather_u8m1x4(vop, lo), curl, 64); +#elif __riscv_v_fixed_vlen == 256 + vboolx64_t mws = __riscv_vmseq(simdutf_vrgather_u8m1x2(vws, lo), in, 64); + vboolx64_t mop = __riscv_vmseq(simdutf_vrgather_u8m1x2(vop, lo), curl, 64); +#else + vboolx64_t mws = __riscv_vmseq(__riscv_vrgather(vws, lo, 64), in, 64); + vboolx64_t mop = __riscv_vmseq(__riscv_vrgather(vop, lo, 64), curl, 64); +#endif - simdjson_inline bool is_ascii(const simd8x64& input) { - return input.reduce_or().is_ascii(); - } + return { + __riscv_vmv_x(__riscv_vreinterpret_u64m1(mws)), + __riscv_vmv_x(__riscv_vreinterpret_u64m1(mop)) + }; +} - simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { - simd8 is_third_byte = prev2.saturating_sub(0xe0u - 0x80); // Only 111_____ will be >= 0x80 - simd8 is_fourth_byte = prev3.saturating_sub(0xf0u - 0x80); // Only 1111____ will be >= 0x80 - return is_third_byte | is_fourth_byte; - } +simdjson_inline bool is_ascii(const simd8x64& input) { + return input.is_ascii(); +} - } // unnamed namespace - } // namespace lasx +simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { + simd8 is_third_byte = prev2.saturating_sub(0xe0u-0x80); // Only 111_____ will be >= 0x80 + simd8 is_fourth_byte = prev3.saturating_sub(0xf0u-0x80); // Only 1111____ will be >= 0x80 + return is_third_byte | is_fourth_byte; +} + +} // unnamed namespace +} // namespace rvv_vls } // namespace simdjson // @@ -52226,60 +60797,60 @@ namespace simdjson { // Implementation-specific overrides // namespace simdjson { - namespace lasx { +namespace rvv_vls { - simdjson_warn_unused error_code implementation::minify(const uint8_t* buf, size_t len, uint8_t* dst, size_t& dst_len) const noexcept { - return lasx::stage1::json_minifier::minify<64>(buf, len, dst, dst_len); - } +simdjson_warn_unused error_code implementation::minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept { + return rvv_vls::stage1::json_minifier::minify<64>(buf, len, dst, dst_len); +} - simdjson_warn_unused error_code dom_parser_implementation::stage1(const uint8_t* _buf, size_t _len, stage1_mode streaming) noexcept { - this->buf = _buf; - this->len = _len; - return lasx::stage1::json_structural_indexer::index<64>(buf, len, *this, streaming); - } +simdjson_warn_unused error_code dom_parser_implementation::stage1(const uint8_t *_buf, size_t _len, stage1_mode streaming) noexcept { + this->buf = _buf; + this->len = _len; + return rvv_vls::stage1::json_structural_indexer::index<64>(buf, len, *this, streaming); +} - simdjson_warn_unused bool implementation::validate_utf8(const char* buf, size_t len) const noexcept { - return lasx::stage1::generic_validate_utf8(buf, len); - } +simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept { + return rvv_vls::stage1::generic_validate_utf8(buf,len); +} - simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document& _doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); - } +simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept { + return stage2::tape_builder::parse_document(*this, _doc); +} - simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document& _doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); - } +simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept { + return stage2::tape_builder::parse_document(*this, _doc); +} - simdjson_warn_unused uint8_t* dom_parser_implementation::parse_string(const uint8_t* src, uint8_t* dst, bool allow_replacement) const noexcept { - return lasx::stringparsing::parse_string(src, dst, allow_replacement); - } +SIMDJSON_NO_SANITIZE_MEMORY +simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept { + return rvv_vls::stringparsing::parse_string(src, dst, allow_replacement); +} - simdjson_warn_unused uint8_t* dom_parser_implementation::parse_wobbly_string(const uint8_t* src, uint8_t* dst) const noexcept { - return lasx::stringparsing::parse_wobbly_string(src, dst); - } +simdjson_warn_unused uint8_t *dom_parser_implementation::parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept { + return rvv_vls::stringparsing::parse_wobbly_string(src, dst); +} - simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t* _buf, size_t _len, dom::document& _doc) noexcept { - auto error = stage1(_buf, _len, stage1_mode::regular); - if (error) { return error; } - return stage2(_doc); - } +simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept { + auto error = stage1(_buf, _len, stage1_mode::regular); + if (error) { return error; } + return stage2(_doc); +} - } // namespace lasx +} // namespace rvv_vls } // namespace simdjson -/* including simdjson/lasx/end.h: #include */ -/* begin file simdjson/lasx/end.h */ +/* including simdjson/rvv-vls/end.h: #include */ +/* begin file simdjson/rvv-vls/end.h */ /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #include "simdjson/lasx/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/base.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ -#undef SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT -/* undefining SIMDJSON_IMPLEMENTATION from "lasx" */ +/* undefining SIMDJSON_IMPLEMENTATION from "rvv_vls" */ #undef SIMDJSON_IMPLEMENTATION -/* end file simdjson/lasx/end.h */ +/* end file simdjson/rvv-vls/end.h */ -#endif // SIMDJSON_SRC_LASX_CPP -/* end file lasx.cpp */ +#endif // SIMDJSON_SRC_RVV_VLS_CPP +/* end file rvv-vls.cpp */ #endif #if SIMDJSON_IMPLEMENTATION_FALLBACK /* including fallback.cpp: #include */ @@ -52310,14 +60881,14 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - /** - * Fallback implementation (runs on any machine). - */ - namespace fallback { +/** + * Fallback implementation (runs on any machine). + */ +namespace fallback { - class implementation; +class implementation; - } // namespace fallback +} // namespace fallback } // namespace simdjson #endif // SIMDJSON_FALLBACK_BASE_H @@ -52332,43 +60903,43 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace fallback { - namespace { +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; - } +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) { +/* 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; + 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); + return __builtin_clzll(input_num); #endif// _MSC_VER - } +} - } // unnamed namespace - } // namespace fallback +} // unnamed namespace +} // namespace fallback } // namespace simdjson #endif // SIMDJSON_FALLBACK_BITMANIPULATION_H @@ -52383,31 +60954,49 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace fallback { - namespace { +namespace fallback { +namespace { - // Holds backslashes and quotes locations. - struct backslash_and_quote { - public: - static constexpr uint32_t BYTES_PROCESSED = 1; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t* src, uint8_t* dst); +// 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; } + 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 + 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] }; - } +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] }; +} - } // unnamed namespace - } // namespace fallback + +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 @@ -52425,73 +61014,79 @@ namespace simdjson { #include #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); +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 { +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); - } +// 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(chars)); - } +/** @private */ +static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) { + return parse_eight_digits_unrolled(reinterpret_cast(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; - } +// 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; +/** @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; + // 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 + 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); + __uint128_t r = (static_cast<__uint128_t>(value1)) * value2; + answer.low = uint64_t(r); + answer.high = uint64_t(r >> 64); #endif - return answer; - } + return answer; +} - } // namespace numberparsing - } // namespace fallback +} // 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 */ @@ -52523,10 +61118,12 @@ namespace simdjson { /* 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_LSX */ -/* amalgamation skipped (editor-only): #include "simdjson/lsx/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 */ @@ -52536,22 +61133,22 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace fallback { +namespace fallback { - struct open_container; - class dom_parser_implementation; +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 - }; +/** + * 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 fallback } // namespace simdjson #endif // SIMDJSON_GENERIC_BASE_H @@ -52568,99 +61165,97 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace fallback { - namespace { - namespace jsoncharutils { +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]; - } +// 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]; - } +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; - } +// 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 - } +// 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; - } +// 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 jsoncharutils +} // unnamed namespace +} // namespace fallback } // namespace simdjson #endif // SIMDJSON_GENERIC_JSONCHARUTILS_H @@ -52678,69 +61273,69 @@ namespace simdjson { #include namespace simdjson { - namespace fallback { - namespace { - /// @private - namespace atomparsing { +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; } +// 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; } - // 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); - } +// 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); +} - 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) { + 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_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) { + 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_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) { + 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; } - } +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; } +} - } // namespace atomparsing - } // unnamed namespace - } // namespace fallback +} // namespace atomparsing +} // unnamed namespace +} // namespace fallback } // namespace simdjson #endif // SIMDJSON_GENERIC_ATOMPARSING_H @@ -52756,83 +61351,83 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace fallback { +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 +// 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"); +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_containers{}; - /** Whether each open container is a [ or { */ - std::unique_ptr 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{}; +class dom_parser_implementation final : public internal::dom_parser_implementation { +public: + /** Tape location of each open { or [ */ + std::unique_ptr open_containers{}; + /** Whether each open container is a [ or { */ + std::unique_ptr 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; + 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); + 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 fallback } // namespace simdjson namespace simdjson { - namespace fallback { +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; +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) { return CAPACITY; } - // Stage 1 index output - size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; - 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; +// 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) { return CAPACITY; } + // Stage 1 index output + size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; + 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; - } + _capacity = capacity; + return SUCCESS; +} - inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { - // 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; } +inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { + // 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; - } + _max_depth = max_depth; + return SUCCESS; +} - } // namespace fallback +} // namespace fallback } // namespace simdjson #endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H @@ -52847,129 +61442,148 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace fallback { +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 : public internal::implementation_simdjson_result_base { - * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} - * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} - * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} - * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} - * // Your extra methods here - * } - * - * Then any method returning simdjson_result will be chainable with your methods. - */ - template - struct implementation_simdjson_result_base { +// 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 : public internal::implementation_simdjson_result_base { + * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} + * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} + * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} + * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} + * // Your extra methods here + * } + * + * Then any method returning simdjson_result will be chainable with your methods. + */ +template +struct implementation_simdjson_result_base { - /** - * Create a new empty result with error = UNINITIALIZED. - */ - simdjson_inline implementation_simdjson_result_base() noexcept = default; + /** + * 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 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 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; + /** + * 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 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; + /** + * 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_inline error_code error() const 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& value() & noexcept(false); + /** + * 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); - /** - * Take the result value (move it). - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline T&& value() && 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&& take_value() && noexcept(false); + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& 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); + /** + * 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; - 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 + /** + * 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; - } // namespace fallback + 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 @@ -52990,8 +61604,8 @@ namespace simdjson { #include namespace simdjson { - namespace fallback { - namespace numberparsing { +namespace fallback { +namespace numberparsing { #ifdef JSON_TEST_NUMBERS #define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) @@ -53007,540 +61621,547 @@ namespace simdjson { #define BIGINT_NUMBER(SRC) (BIGINT_ERROR) #endif - namespace { +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(negative)) << 63); - std::memcpy(&d, &mantissa, sizeof(d)); - return d; - } +// 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(negative)) << 63); + std::memcpy(&d, &mantissa, sizeof(d)); + return d; +} - // 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 +// 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) + if (0 <= power && power <= 22 && i <= 9007199254740991) #else - if (-22 <= power && power <= 22 && i <= 9007199254740991) + 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 + { + // 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. + // 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; - } + // 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 - // approximatively 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; + // 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 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. - simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]); - // 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. + // 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 - // 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. - simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); - 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); + // 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. - // 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 - } - } + // 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); - mantissa += mantissa & 1; - mantissa >>= 1; + // 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 + } + } - // 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; - } + mantissa += mantissa & 1; + mantissa >>= 1; - // 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(ptr)); - // We do not accept infinite values. + // 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; +} - // 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::max)() || *outDouble < std::numeric_limits::lowest()); - } +// 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(ptr)); + // We do not accept infinite values. - static bool parse_float_fallback(const uint8_t* ptr, const uint8_t* end_ptr, double* outDouble) { - *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(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::max)() || *outDouble < std::numeric_limits::lowest()); +} - // 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::max)() || *outDouble < std::numeric_limits::lowest()); - } +static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) { + *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(end_ptr)); + // We do not accept infinite values. - // 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); - } + // 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::max)() || *outDouble < std::numeric_limits::lowest()); +} - template - 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(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; - } +// 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); +} - simdjson_inline bool is_digit(const uint8_t c) { - return static_cast(c - '0') <= 9; - } +template +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(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 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; +simdjson_inline bool is_digit(const uint8_t c) { + return static_cast(c - '0') <= 9; +} + +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; #ifdef SIMDJSON_SWAR_NUMBER_PARSING #if SIMDJSON_SWAR_NUMBER_PARSING - // this helps if we have lots of decimals! - // this turns out to be frequent enough. - if (is_made_of_eight_digits_fast(p)) { - i = i * 100000000 + parse_eight_digits_unrolled(p); - p += 8; - } + // this helps if we have lots of decimals! + // this turns out to be frequent enough. + if (is_made_of_eight_digits_fast(p)) { + i = i * 100000000 + parse_eight_digits_unrolled(p); + p += 8; + } #endif // SIMDJSON_SWAR_NUMBER_PARSING #endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING - // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) - if (parse_digit(*p, i)) { ++p; } - while (parse_digit(*p, i)) { p++; } - exponent = first_after_period - p; - // Decimal without digits (123.) is illegal - if (exponent == 0) { - return INVALID_NUMBER(src); - } - return SUCCESS; - } + // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) + if (parse_digit(*p, i)) { ++p; } + while (parse_digit(*p, i)) { p++; } + exponent = first_after_period - p; + // Decimal without digits (123.) is illegal + if (exponent == 0) { + return INVALID_NUMBER(src); + } + return SUCCESS; +} - 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 +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. + // 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. + // 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 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; - } + // 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 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); - } +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 +} // unnamed namespace - /** @private */ - static 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 */ +static 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 - 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; - } +/** @private */ +template +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 - simdjson_inline error_code parse_number(const uint8_t* const src, W& writer); +// 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 +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 +// for performance analysis, it is sometimes useful to skip parsing #ifdef SIMDJSON_SKIPNUMBERPARSING - template - simdjson_inline error_code parse_number(const uint8_t* const, W& writer) { - writer.append_s64(0); // always write zero - return SUCCESS; // always succeeds - } +template +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 parse_unsigned(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline bool is_negative(const uint8_t* src) noexcept { return false; } - simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t* src) noexcept { return false; } - simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t* src) noexcept { return number_type::signed_integer; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t * src) noexcept { return number_type::signed_integer; } #else // parse the number at src @@ -53552,744 +62173,753 @@ namespace simdjson { // content and append a space before calling this function. // // Our objective is accurate parsing (ULP of 0) at high speed. - template - 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)) { 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 INVALID_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 { - WRITE_INTEGER(negative ? (~i + 1) : i, src, writer); - } - 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 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; - 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); - // 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 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 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; - 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); - // 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 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; - 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); - // 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 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 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; - while (parse_digit(*src, i)) { src++; } - - // 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 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; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (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 == '.')) { - p++; - const uint8_t* start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (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 = 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; - } - - simdjson_unused simdjson_inline bool is_negative(const uint8_t* src) noexcept { - return (*src == '-'); - } - - simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t* src) noexcept { - bool negative = (*src == '-'); - src += uint8_t(negative); - const uint8_t* p = src; - while (static_cast(*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 get_number_type(const uint8_t* src) noexcept { - bool negative = (*src == '-'); - src += uint8_t(negative); - const uint8_t* p = src; - while (static_cast(*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("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; - } - 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("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 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 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; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (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 == '.')) { - p++; - const uint8_t* start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (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 = 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; - } - - } // unnamed namespace +template +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)) { 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 INVALID_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 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; + 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); + // 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 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 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; + 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); + // 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 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; + 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); + // 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 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 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; + while (parse_digit(*src, i)) { src++; } + + // 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { + return (*src == '-'); +} + +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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 get_number_type(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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("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("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 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +} // unnamed namespace #endif // SIMDJSON_SKIPNUMBERPARSING - } // namespace numberparsing +} // 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; - } +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 fallback } // namespace simdjson #endif // SIMDJSON_GENERIC_NUMBERPARSING_H @@ -54306,84 +62936,113 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace fallback { +namespace fallback { - // - // internal::implementation_simdjson_result_base inline implementation - // +// +// internal::implementation_simdjson_result_base inline implementation +// - template - simdjson_inline void implementation_simdjson_result_base::tie(T& value, error_code& error) && noexcept { - error = this->second; - if (!error) { - value = std::forward>(*this).first; - } - } +template +simdjson_inline void implementation_simdjson_result_base::tie(T &value, error_code &error) && noexcept { + error = this->second; + if (!error) { + value = std::forward>(*this).first; + } +} - template - simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T& value) && noexcept { - error_code error; - std::forward>(*this).tie(value, error); - return error; - } +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T &value) && noexcept { + error_code error; + std::forward>(*this).tie(value, error); + return error; +} - template - simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { - return this->second; - } +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { + return this->second; +} + + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} #if SIMDJSON_EXCEPTIONS - template - simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return this->first; - } +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} - template - simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { - return std::forward>(*this).take_value(); - } +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} - template - simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return std::forward(this->first); - } +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} - template - simdjson_inline implementation_simdjson_result_base::operator T && () && noexcept(false) { - return std::forward>(*this).take_value(); - } + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + +template +simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return this->first; +} + +template +simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { + return std::forward>(*this).take_value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return std::forward(this->first); +} + +template +simdjson_inline implementation_simdjson_result_base::operator T&&() && noexcept(false) { + return std::forward>(*this).take_value(); +} #endif // SIMDJSON_EXCEPTIONS - template - simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { - return this->first; - } +template +simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { + return this->first; +} - template - simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { - return this->first; - } +template +simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { + return this->first; +} - template - simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { - return std::forward(this->first); - } +template +simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { + return std::forward(this->first); +} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T&& value, error_code error) noexcept - : first{ std::forward(value) }, second{ error } {} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept - : implementation_simdjson_result_base(T{}, error) {} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T&& value) noexcept - : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value, error_code error) noexcept + : first{std::forward(value)}, second{error} {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept + : implementation_simdjson_result_base(T{}, error) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value) noexcept + : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} - } // namespace fallback +} // namespace fallback } // namespace simdjson #endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H @@ -54412,28 +63071,28 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace fallback { +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& 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; - }; +/** + * @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& 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 fallback } // namespace simdjson #endif // SIMDJSON_FALLBACK_IMPLEMENTATION_H @@ -54453,14 +63112,14 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - /** - * Fallback implementation (runs on any machine). - */ - namespace fallback { +/** + * Fallback implementation (runs on any machine). + */ +namespace fallback { - class implementation; +class implementation; - } // namespace fallback +} // namespace fallback } // namespace simdjson #endif // SIMDJSON_FALLBACK_BASE_H @@ -54475,43 +63134,43 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace fallback { - namespace { +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; - } +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) { +/* 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; + 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); + return __builtin_clzll(input_num); #endif// _MSC_VER - } +} - } // unnamed namespace - } // namespace fallback +} // unnamed namespace +} // namespace fallback } // namespace simdjson #endif // SIMDJSON_FALLBACK_BITMANIPULATION_H @@ -54526,31 +63185,49 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace fallback { - namespace { +namespace fallback { +namespace { - // Holds backslashes and quotes locations. - struct backslash_and_quote { - public: - static constexpr uint32_t BYTES_PROCESSED = 1; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t* src, uint8_t* dst); +// 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; } + 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 + 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] }; - } +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] }; +} - } // unnamed namespace - } // namespace fallback + +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 @@ -54568,73 +63245,79 @@ namespace simdjson { #include #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); +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 { +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); - } +// 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(chars)); - } +/** @private */ +static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) { + return parse_eight_digits_unrolled(reinterpret_cast(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; - } +// 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; +/** @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; + // 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 + 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); + __uint128_t r = (static_cast<__uint128_t>(value1)) * value2; + answer.low = uint64_t(r); + answer.high = uint64_t(r >> 64); #endif - return answer; - } + return answer; +} - } // namespace numberparsing - } // namespace fallback +} // 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 */ @@ -54650,105 +63333,106 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace fallback { - namespace { - namespace stage1 { +namespace fallback { +namespace { +namespace stage1 { - /** - * This algorithm is used to quickly identify the last structural position that - * makes up a complete document. - * - * It does this by going backwards and finding the last *document boundary* (a - * place where one value follows another without a comma between them). If the - * last document (the characters after the boundary) has an equal number of - * start and end brackets, it is considered complete. - * - * Simply put, we iterate over the structural characters, starting from - * the end. We consider that we found the end of a JSON document when the - * first element of the pair is NOT one of these characters: '{' '[' ':' ',' - * and when the second element is NOT one of these characters: '}' ']' ':' ','. - * - * This simple comparison works most of the time, but it does not cover cases - * where the batch's structural indexes contain a perfect amount of documents. - * In such a case, we do not have access to the structural index which follows - * the last document, therefore, we do not have access to the second element in - * the pair, and that means we cannot identify the last document. To fix this - * issue, we keep a count of the open and closed curly/square braces we found - * while searching for the pair. When we find a pair AND the count of open and - * closed curly/square braces is the same, we know that we just passed a - * complete document, therefore the last json buffer location is the end of the - * batch. - */ - simdjson_inline uint32_t find_next_document_index(dom_parser_implementation& parser) { - // Variant: do not count separately, just figure out depth - if (parser.n_structural_indexes == 0) { return 0; } - auto arr_cnt = 0; - auto obj_cnt = 0; - for (auto i = parser.n_structural_indexes - 1; i > 0; i--) { - auto idxb = parser.structural_indexes[i]; - switch (parser.buf[idxb]) { - case ':': - case ',': - continue; - case '}': - obj_cnt--; - continue; - case ']': - arr_cnt--; - continue; - case '{': - obj_cnt++; - break; - case '[': - arr_cnt++; - break; - } - auto idxa = parser.structural_indexes[i - 1]; - switch (parser.buf[idxa]) { - case '{': - case '[': - case ':': - case ',': - continue; - } - // Last document is complete, so the next document will appear after! - if (!arr_cnt && !obj_cnt) { - return parser.n_structural_indexes; - } - // Last document is incomplete; mark the document at i + 1 as the next one - return i; - } - // If we made it to the end, we want to finish counting to see if we have a full document. - switch (parser.buf[parser.structural_indexes[0]]) { - case '}': - obj_cnt--; - break; - case ']': - arr_cnt--; - break; - case '{': - obj_cnt++; - break; - case '[': - arr_cnt++; - break; - } - if (!arr_cnt && !obj_cnt) { - // We have a complete document. - return parser.n_structural_indexes; - } - return 0; - } +/** + * This algorithm is used to quickly identify the last structural position that + * makes up a complete document. + * + * It does this by going backwards and finding the last *document boundary* (a + * place where one value follows another without a comma between them). If the + * last document (the characters after the boundary) has an equal number of + * start and end brackets, it is considered complete. + * + * Simply put, we iterate over the structural characters, starting from + * the end. We consider that we found the end of a JSON document when the + * first element of the pair is NOT one of these characters: '{' '[' ':' ',' + * and when the second element is NOT one of these characters: '}' ']' ':' ','. + * + * This simple comparison works most of the time, but it does not cover cases + * where the batch's structural indexes contain a perfect amount of documents. + * In such a case, we do not have access to the structural index which follows + * the last document, therefore, we do not have access to the second element in + * the pair, and that means we cannot identify the last document. To fix this + * issue, we keep a count of the open and closed curly/square braces we found + * while searching for the pair. When we find a pair AND the count of open and + * closed curly/square braces is the same, we know that we just passed a + * complete document, therefore the last json buffer location is the end of the + * batch. + */ +simdjson_inline uint32_t find_next_document_index(dom_parser_implementation &parser) { + // Variant: do not count separately, just figure out depth + if(parser.n_structural_indexes == 0) { return 0; } + auto arr_cnt = 0; + auto obj_cnt = 0; + for (auto i = parser.n_structural_indexes - 1; i > 0; i--) { + auto idxb = parser.structural_indexes[i]; + switch (parser.buf[idxb]) { + case ':': + case ',': + continue; + case '}': + obj_cnt--; + continue; + case ']': + arr_cnt--; + continue; + case '{': + obj_cnt++; + break; + case '[': + arr_cnt++; + break; + } + auto idxa = parser.structural_indexes[i - 1]; + switch (parser.buf[idxa]) { + case '{': + case '[': + case ':': + case ',': + continue; + } + // Last document is complete, so the next document will appear after! + if (!arr_cnt && !obj_cnt) { + return parser.n_structural_indexes; + } + // Last document is incomplete; mark the document at i + 1 as the next one + return i; + } + // If we made it to the end, we want to finish counting to see if we have a full document. + switch (parser.buf[parser.structural_indexes[0]]) { + case '}': + obj_cnt--; + break; + case ']': + arr_cnt--; + break; + case '{': + obj_cnt++; + break; + case '[': + arr_cnt++; + break; + } + if (!arr_cnt && !obj_cnt) { + // We have a complete document. + return parser.n_structural_indexes; + } + return 0; +} - } // namespace stage1 - } // unnamed namespace - } // namespace fallback +} // namespace stage1 +} // unnamed namespace +} // namespace fallback } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE1_FIND_NEXT_DOCUMENT_INDEX_H /* end file generic/stage1/find_next_document_index.h for fallback */ /* including generic/stage2/stringparsing.h for fallback: #include */ /* begin file generic/stage2/stringparsing.h for fallback */ +#include #ifndef SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ @@ -54761,242 +63445,238 @@ namespace simdjson { // It is intended to be included multiple times and compiled multiple times namespace simdjson { - namespace fallback { - namespace { - /// @private - namespace stringparsing { +namespace fallback { +namespace { +/// @private +namespace stringparsing { - // begin copypasta - // These chars yield themselves: " \ / - // b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab - // u not handled in this table as it's complex - static const uint8_t escape_map[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// begin copypasta +// These chars yield themselves: " \ / +// b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab +// u not handled in this table as it's complex +static const uint8_t escape_map[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0. + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5. - 0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6. - 0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7. + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4. + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5. + 0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6. + 0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - }; + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; - // handle a unicode codepoint - // write appropriate values into dest - // src will advance 6 bytes or 12 bytes - // dest will advance a variable amount (return via pointer) - // return true if the unicode codepoint was valid - // We work in little-endian then swap at write time - simdjson_warn_unused - simdjson_inline bool handle_unicode_codepoint(const uint8_t** src_ptr, - uint8_t** dst_ptr, bool allow_replacement) { - // Use the default Unicode Character 'REPLACEMENT CHARACTER' (U+FFFD) - constexpr uint32_t substitution_code_point = 0xfffd; - // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the - // conversion is not valid; we defer the check for this to inside the - // multilingual plane check. - uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); - *src_ptr += 6; +// handle a unicode codepoint +// write appropriate values into dest +// src will advance 6 bytes or 12 bytes +// dest will advance a variable amount (return via pointer) +// return true if the unicode codepoint was valid +// We work in little-endian then swap at write time +simdjson_warn_unused +simdjson_inline bool handle_unicode_codepoint(const uint8_t **src_ptr, + uint8_t **dst_ptr, bool allow_replacement) { + // Use the default Unicode Character 'REPLACEMENT CHARACTER' (U+FFFD) + constexpr uint32_t substitution_code_point = 0xfffd; + // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the + // conversion is not valid; we defer the check for this to inside the + // multilingual plane check. + uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); + *src_ptr += 6; - // If we found a high surrogate, we must - // check for low surrogate for characters - // outside the Basic - // Multilingual Plane. - if (code_point >= 0xd800 && code_point < 0xdc00) { - const uint8_t* src_data = *src_ptr; - /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ - if (((src_data[0] << 8) | src_data[1]) != ((static_cast ('\\') << 8) | static_cast ('u'))) { - if (!allow_replacement) { return false; } - code_point = substitution_code_point; - } - else { - uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); + // If we found a high surrogate, we must + // check for low surrogate for characters + // outside the Basic + // Multilingual Plane. + if (code_point >= 0xd800 && code_point < 0xdc00) { + const uint8_t *src_data = *src_ptr; + /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ + if (((src_data[0] << 8) | src_data[1]) != ((static_cast ('\\') << 8) | static_cast ('u'))) { + if(!allow_replacement) { return false; } + code_point = substitution_code_point; + } else { + uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); - // We have already checked that the high surrogate is valid and - // (code_point - 0xd800) < 1024. - // - // Check that code_point_2 is in the range 0xdc00..0xdfff - // and that code_point_2 was parsed from valid hex. - uint32_t low_bit = code_point_2 - 0xdc00; - if (low_bit >> 10) { - if (!allow_replacement) { return false; } - code_point = substitution_code_point; - } - else { - code_point = (((code_point - 0xd800) << 10) | low_bit) + 0x10000; - *src_ptr += 6; - } + // We have already checked that the high surrogate is valid and + // (code_point - 0xd800) < 1024. + // + // Check that code_point_2 is in the range 0xdc00..0xdfff + // and that code_point_2 was parsed from valid hex. + uint32_t low_bit = code_point_2 - 0xdc00; + if (low_bit >> 10) { + if(!allow_replacement) { return false; } + code_point = substitution_code_point; + } else { + code_point = (((code_point - 0xd800) << 10) | low_bit) + 0x10000; + *src_ptr += 6; + } - } - } - else if (code_point >= 0xdc00 && code_point <= 0xdfff) { - // If we encounter a low surrogate (not preceded by a high surrogate) - // then we have an error. - if (!allow_replacement) { return false; } - code_point = substitution_code_point; - } - size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); - *dst_ptr += offset; - return offset > 0; - } + } + } else if (code_point >= 0xdc00 && code_point <= 0xdfff) { + // If we encounter a low surrogate (not preceded by a high surrogate) + // then we have an error. + if(!allow_replacement) { return false; } + code_point = substitution_code_point; + } + size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); + *dst_ptr += offset; + return offset > 0; +} - // handle a unicode codepoint using the wobbly convention - // https://simonsapin.github.io/wtf-8/ - // write appropriate values into dest - // src will advance 6 bytes or 12 bytes - // dest will advance a variable amount (return via pointer) - // return true if the unicode codepoint was valid - // We work in little-endian then swap at write time - simdjson_warn_unused - simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t** src_ptr, - uint8_t** dst_ptr) { - // It is not ideal that this function is nearly identical to handle_unicode_codepoint. - // - // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the - // conversion is not valid; we defer the check for this to inside the - // multilingual plane check. - uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); - *src_ptr += 6; - // If we found a high surrogate, we must - // check for low surrogate for characters - // outside the Basic - // Multilingual Plane. - if (code_point >= 0xd800 && code_point < 0xdc00) { - const uint8_t* src_data = *src_ptr; - /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ - if (((src_data[0] << 8) | src_data[1]) == ((static_cast ('\\') << 8) | static_cast ('u'))) { - uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); - uint32_t low_bit = code_point_2 - 0xdc00; - if ((low_bit >> 10) == 0) { - code_point = - (((code_point - 0xd800) << 10) | low_bit) + 0x10000; - *src_ptr += 6; - } - } - } +// handle a unicode codepoint using the wobbly convention +// https://simonsapin.github.io/wtf-8/ +// write appropriate values into dest +// src will advance 6 bytes or 12 bytes +// dest will advance a variable amount (return via pointer) +// return true if the unicode codepoint was valid +// We work in little-endian then swap at write time +simdjson_warn_unused +simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t **src_ptr, + uint8_t **dst_ptr) { + // It is not ideal that this function is nearly identical to handle_unicode_codepoint. + // + // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the + // conversion is not valid; we defer the check for this to inside the + // multilingual plane check. + uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); + *src_ptr += 6; + // If we found a high surrogate, we must + // check for low surrogate for characters + // outside the Basic + // Multilingual Plane. + if (code_point >= 0xd800 && code_point < 0xdc00) { + const uint8_t *src_data = *src_ptr; + /* Compiler optimizations convert this to a single 16-bit load and compare on most platforms */ + if (((src_data[0] << 8) | src_data[1]) == ((static_cast ('\\') << 8) | static_cast ('u'))) { + uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(src_data + 2); + uint32_t low_bit = code_point_2 - 0xdc00; + if ((low_bit >> 10) == 0) { + code_point = + (((code_point - 0xd800) << 10) | low_bit) + 0x10000; + *src_ptr += 6; + } + } + } - size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); - *dst_ptr += offset; - return offset > 0; - } + size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); + *dst_ptr += offset; + return offset > 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. - */ - simdjson_warn_unused simdjson_inline uint8_t* parse_string(const uint8_t* src, uint8_t* dst, bool allow_replacement) { - while (1) { - // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); - // If the next thing is the end quote, copy and return - if (bs_quote.has_quote_first()) { - // we encountered quotes first. Move dst to point to quotes and exit - return dst + bs_quote.quote_index(); - } - if (bs_quote.has_backslash()) { - /* find out where the backspace is */ - auto bs_dist = bs_quote.backslash_index(); - uint8_t escape_char = src[bs_dist + 1]; - /* we encountered backslash first. Handle backslash */ - if (escape_char == 'u') { - /* move src/dst up to the start; they will be further adjusted - within the unicode codepoint handling code. */ - src += bs_dist; - dst += bs_dist; - if (!handle_unicode_codepoint(&src, &dst, allow_replacement)) { - return nullptr; - } - } - else { - /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and - * write bs_dist+1 characters to output - * note this may reach beyond the part of the buffer we've actually - * seen. I think this is ok */ - uint8_t escape_result = escape_map[escape_char]; - if (escape_result == 0u) { - return nullptr; /* bogus escape value is an error */ - } - dst[bs_dist] = escape_result; - src += bs_dist + 2; - dst += bs_dist + 1; - } - } - else { - /* they are the same. Since they can't co-occur, it means we - * encountered neither. */ - src += backslash_and_quote::BYTES_PROCESSED; - dst += backslash_and_quote::BYTES_PROCESSED; - } - } - } +/** + * 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. + */ +simdjson_warn_unused simdjson_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) { + while (1) { + // Copy the next n bytes, and find the backslash and quote in them. + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); + // If the next thing is the end quote, copy and return + if (bs_quote.has_quote_first()) { + // we encountered quotes first. Move dst to point to quotes and exit + return dst + bs_quote.quote_index(); + } + if (bs_quote.has_backslash()) { + /* find out where the backspace is */ + auto bs_dist = bs_quote.backslash_index(); + uint8_t escape_char = src[bs_dist + 1]; + /* we encountered backslash first. Handle backslash */ + if (escape_char == 'u') { + /* move src/dst up to the start; they will be further adjusted + within the unicode codepoint handling code. */ + src += bs_dist; + dst += bs_dist; + if (!handle_unicode_codepoint(&src, &dst, allow_replacement)) { + return nullptr; + } + } else { + /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and + * write bs_dist+1 characters to output + * note this may reach beyond the part of the buffer we've actually + * seen. I think this is ok */ + uint8_t escape_result = escape_map[escape_char]; + if (escape_result == 0u) { + return nullptr; /* bogus escape value is an error */ + } + dst[bs_dist] = escape_result; + src += bs_dist + 2; + dst += bs_dist + 1; + } + } else { + /* they are the same. Since they can't co-occur, it means we + * encountered neither. */ + src += backslash_and_quote::BYTES_PROCESSED; + dst += backslash_and_quote::BYTES_PROCESSED; + } + } +} - simdjson_warn_unused simdjson_inline uint8_t* parse_wobbly_string(const uint8_t* src, uint8_t* dst) { - // It is not ideal that this function is nearly identical to parse_string. - while (1) { - // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); - // If the next thing is the end quote, copy and return - if (bs_quote.has_quote_first()) { - // we encountered quotes first. Move dst to point to quotes and exit - return dst + bs_quote.quote_index(); - } - if (bs_quote.has_backslash()) { - /* find out where the backspace is */ - auto bs_dist = bs_quote.backslash_index(); - uint8_t escape_char = src[bs_dist + 1]; - /* we encountered backslash first. Handle backslash */ - if (escape_char == 'u') { - /* move src/dst up to the start; they will be further adjusted - within the unicode codepoint handling code. */ - src += bs_dist; - dst += bs_dist; - if (!handle_unicode_codepoint_wobbly(&src, &dst)) { - return nullptr; - } - } - else { - /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and - * write bs_dist+1 characters to output - * note this may reach beyond the part of the buffer we've actually - * seen. I think this is ok */ - uint8_t escape_result = escape_map[escape_char]; - if (escape_result == 0u) { - return nullptr; /* bogus escape value is an error */ - } - dst[bs_dist] = escape_result; - src += bs_dist + 2; - dst += bs_dist + 1; - } - } - else { - /* they are the same. Since they can't co-occur, it means we - * encountered neither. */ - src += backslash_and_quote::BYTES_PROCESSED; - dst += backslash_and_quote::BYTES_PROCESSED; - } - } - } +simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) { + // It is not ideal that this function is nearly identical to parse_string. + while (1) { + // Copy the next n bytes, and find the backslash and quote in them. + auto b = backslash_and_quote{}; + auto bs_quote = b.copy_and_find(src, dst); + // If the next thing is the end quote, copy and return + if (bs_quote.has_quote_first()) { + // we encountered quotes first. Move dst to point to quotes and exit + return dst + bs_quote.quote_index(); + } + if (bs_quote.has_backslash()) { + /* find out where the backspace is */ + auto bs_dist = bs_quote.backslash_index(); + uint8_t escape_char = src[bs_dist + 1]; + /* we encountered backslash first. Handle backslash */ + if (escape_char == 'u') { + /* move src/dst up to the start; they will be further adjusted + within the unicode codepoint handling code. */ + src += bs_dist; + dst += bs_dist; + if (!handle_unicode_codepoint_wobbly(&src, &dst)) { + return nullptr; + } + } else { + /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and + * write bs_dist+1 characters to output + * note this may reach beyond the part of the buffer we've actually + * seen. I think this is ok */ + uint8_t escape_result = escape_map[escape_char]; + if (escape_result == 0u) { + return nullptr; /* bogus escape value is an error */ + } + dst[bs_dist] = escape_result; + src += bs_dist + 2; + dst += bs_dist + 1; + } + } else { + /* they are the same. Since they can't co-occur, it means we + * encountered neither. */ + src += backslash_and_quote::BYTES_PROCESSED; + dst += backslash_and_quote::BYTES_PROCESSED; + } + } +} - } // namespace stringparsing - } // unnamed namespace - } // namespace fallback +} // namespace stringparsing + +} // unnamed namespace +} // namespace fallback } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_STRINGPARSING_H @@ -55016,92 +63696,90 @@ namespace simdjson { // This is for an internal-only stage 2 specific logger. // Set LOG_ENABLED = true to log what stage 2 is doing! namespace simdjson { - namespace fallback { - namespace { - namespace logger { +namespace fallback { +namespace { +namespace logger { - static constexpr const char* DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"; + static constexpr const char * DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"; #if SIMDJSON_VERBOSE_LOGGING - static constexpr const bool LOG_ENABLED = true; + static constexpr const bool LOG_ENABLED = true; #else - static constexpr const bool LOG_ENABLED = false; + static constexpr const bool LOG_ENABLED = false; #endif - 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 constexpr const int LOG_INDEX_LEN = 5; + 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 constexpr const int LOG_INDEX_LEN = 5; - static int log_depth; // Not threadsafe. Log only. + static int log_depth; // Not threadsafe. Log only. - // Helper to turn unprintable or newline characters into spaces - static simdjson_inline char printable_char(char c) { - if (c >= 0x20) { - return c; - } - else { - return ' '; - } - } + // Helper to turn unprintable or newline characters into spaces + static simdjson_inline char printable_char(char c) { + if (c >= 0x20) { + return c; + } else { + return ' '; + } + } - // Print the header and set up log_start - static simdjson_inline void log_start() { - if (LOG_ENABLED) { - log_depth = 0; - printf("\n"); - printf("| %-*s | %-*s | %-*s | %-*s | Detail |\n", LOG_EVENT_LEN, "Event", LOG_BUFFER_LEN, "Buffer", LOG_SMALL_BUFFER_LEN, "Next", 5, "Next#"); - printf("|%.*s|%.*s|%.*s|%.*s|--------|\n", LOG_EVENT_LEN + 2, DASHES, LOG_BUFFER_LEN + 2, DASHES, LOG_SMALL_BUFFER_LEN + 2, DASHES, 5 + 2, DASHES); - } - } + // Print the header and set up log_start + static simdjson_inline void log_start() { + if (LOG_ENABLED) { + log_depth = 0; + printf("\n"); + printf("| %-*s | %-*s | %-*s | %-*s | Detail |\n", LOG_EVENT_LEN, "Event", LOG_BUFFER_LEN, "Buffer", LOG_SMALL_BUFFER_LEN, "Next", 5, "Next#"); + printf("|%.*s|%.*s|%.*s|%.*s|--------|\n", LOG_EVENT_LEN+2, DASHES, LOG_BUFFER_LEN+2, DASHES, LOG_SMALL_BUFFER_LEN+2, DASHES, 5+2, DASHES); + } + } - simdjson_unused static simdjson_inline void log_string(const char* message) { - if (LOG_ENABLED) { - printf("%s\n", message); - } - } + simdjson_unused static simdjson_inline void log_string(const char *message) { + if (LOG_ENABLED) { + printf("%s\n", message); + } + } - // Logs a single line from the stage 2 DOM parser - template - static simdjson_inline void log_line(S& structurals, const char* title_prefix, const char* title, const char* detail) { - if (LOG_ENABLED) { - printf("| %*s%s%-*s ", log_depth * 2, "", title_prefix, LOG_EVENT_LEN - log_depth * 2 - int(strlen(title_prefix)), title); - auto current_index = structurals.at_beginning() ? nullptr : structurals.next_structural - 1; - auto next_index = structurals.next_structural; - auto current = current_index ? &structurals.buf[*current_index] : reinterpret_cast(" "); - auto next = &structurals.buf[*next_index]; - { - // Print the next N characters in the buffer. - printf("| "); - // Otherwise, print the characters starting from the buffer position. - // Print spaces for unprintable or newline characters. - for (int i = 0; i < LOG_BUFFER_LEN; i++) { - printf("%c", printable_char(current[i])); - } - printf(" "); - // Print the next N characters in the buffer. - printf("| "); - // Otherwise, print the characters starting from the buffer position. - // Print spaces for unprintable or newline characters. - for (int i = 0; i < LOG_SMALL_BUFFER_LEN; i++) { - printf("%c", printable_char(next[i])); - } - printf(" "); - } - if (current_index) { - printf("| %*u ", LOG_INDEX_LEN, *current_index); - } - else { - printf("| %-*s ", LOG_INDEX_LEN, ""); - } - // printf("| %*u ", LOG_INDEX_LEN, structurals.next_tape_index()); - printf("| %-s ", detail); - printf("|\n"); - } - } + // Logs a single line from the stage 2 DOM parser + template + static simdjson_inline void log_line(S &structurals, const char *title_prefix, const char *title, const char *detail) { + if (LOG_ENABLED) { + printf("| %*s%s%-*s ", log_depth*2, "", title_prefix, LOG_EVENT_LEN - log_depth*2 - int(strlen(title_prefix)), title); + auto current_index = structurals.at_beginning() ? nullptr : structurals.next_structural-1; + auto next_index = structurals.next_structural; + auto current = current_index ? &structurals.buf[*current_index] : reinterpret_cast(" "); + auto next = &structurals.buf[*next_index]; + { + // Print the next N characters in the buffer. + printf("| "); + // Otherwise, print the characters starting from the buffer position. + // Print spaces for unprintable or newline characters. + for (int i=0;i - simdjson_warn_unused simdjson_inline error_code walk_document(V& visitor) noexcept; + /** + * Walk the JSON document. + * + * The visitor receives callbacks when values are encountered. All callbacks pass the iterator as + * the first parameter; some callbacks have other parameters as well: + * + * - visit_document_start() - at the beginning. + * - visit_document_end() - at the end (if things were successful). + * + * - visit_array_start() - at the start `[` of a non-empty array. + * - visit_array_end() - at the end `]` of a non-empty array. + * - visit_empty_array() - when an empty array is encountered. + * + * - visit_object_end() - at the start `]` of a non-empty object. + * - visit_object_start() - at the end `]` of a non-empty object. + * - visit_empty_object() - when an empty object is encountered. + * - visit_key(const uint8_t *key) - when a key in an object field is encountered. key is + * guaranteed to point at the first quote of the string (`"key"`). + * - visit_primitive(const uint8_t *value) - when a value is a string, number, boolean or null. + * - visit_root_primitive(iter, uint8_t *value) - when the top-level value is a string, number, boolean or null. + * + * - increment_count(iter) - each time a value is found in an array or object. + */ + template + simdjson_warn_unused simdjson_inline error_code walk_document(V &visitor) noexcept; - /** - * Create an iterator capable of walking a JSON document. - * - * The document must have already passed through stage 1. - */ - simdjson_inline json_iterator(dom_parser_implementation& _dom_parser, size_t start_structural_index); + /** + * Create an iterator capable of walking a JSON document. + * + * The document must have already passed through stage 1. + */ + simdjson_inline json_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index); - /** - * Look at the next token. - * - * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). - * - * They may include invalid JSON as well (such as `1.2.3` or `ture`). - */ - simdjson_inline const uint8_t* peek() const noexcept; - /** - * Advance to the next token. - * - * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). - * - * They may include invalid JSON as well (such as `1.2.3` or `ture`). - */ - simdjson_inline const uint8_t* advance() noexcept; - /** - * Get the remaining length of the document, from the start of the current token. - */ - simdjson_inline size_t remaining_len() const noexcept; - /** - * Check if we are at the end of the document. - * - * If this is true, there are no more tokens. - */ - simdjson_inline bool at_eof() const noexcept; - /** - * Check if we are at the beginning of the document. - */ - simdjson_inline bool at_beginning() const noexcept; - simdjson_inline uint8_t last_structural() const noexcept; + /** + * Look at the next token. + * + * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). + * + * They may include invalid JSON as well (such as `1.2.3` or `ture`). + */ + simdjson_inline const uint8_t *peek() const noexcept; + /** + * Advance to the next token. + * + * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). + * + * They may include invalid JSON as well (such as `1.2.3` or `ture`). + */ + simdjson_inline const uint8_t *advance() noexcept; + /** + * Get the remaining length of the document, from the start of the current token. + */ + simdjson_inline size_t remaining_len() const noexcept; + /** + * Check if we are at the end of the document. + * + * If this is true, there are no more tokens. + */ + simdjson_inline bool at_eof() const noexcept; + /** + * Check if we are at the beginning of the document. + */ + simdjson_inline bool at_beginning() const noexcept; + simdjson_inline uint8_t last_structural() const noexcept; - /** - * Log that a value has been found. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_value(const char* type) const noexcept; - /** - * Log the start of a multipart value. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_start_value(const char* type) const noexcept; - /** - * Log the end of a multipart value. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_end_value(const char* type) const noexcept; - /** - * Log an error. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_error(const char* error) const noexcept; + /** + * Log that a value has been found. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_value(const char *type) const noexcept; + /** + * Log the start of a multipart value. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_start_value(const char *type) const noexcept; + /** + * Log the end of a multipart value. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_end_value(const char *type) const noexcept; + /** + * Log an error. + * + * Set LOG_ENABLED=true in logger.h to see logging. + */ + simdjson_inline void log_error(const char *error) const noexcept; - template - simdjson_warn_unused simdjson_inline error_code visit_root_primitive(V& visitor, const uint8_t* value) noexcept; - template - simdjson_warn_unused simdjson_inline error_code visit_primitive(V& visitor, const uint8_t* value) noexcept; - }; + template + simdjson_warn_unused simdjson_inline error_code visit_root_primitive(V &visitor, const uint8_t *value) noexcept; + template + simdjson_warn_unused simdjson_inline error_code visit_primitive(V &visitor, const uint8_t *value) noexcept; +}; - template - simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V& visitor) noexcept { - logger::log_start(); +template +simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &visitor) noexcept { + logger::log_start(); - // - // Start the document - // - if (at_eof()) { return EMPTY; } - log_start_value("document"); - SIMDJSON_TRY(visitor.visit_document_start(*this)); + // + // Start the document + // + if (at_eof()) { return EMPTY; } + log_start_value("document"); + SIMDJSON_TRY( visitor.visit_document_start(*this) ); - // - // Read first value - // - { - auto value = advance(); + // + // Read first value + // + { + auto value = advance(); - // Make sure the outer object or array is closed before continuing; otherwise, there are ways we - // could get into memory corruption. See https://github.com/simdjson/simdjson/issues/906 - if (!STREAMING) { - switch (*value) { - case '{': if (last_structural() != '}') { log_value("starting brace unmatched"); return TAPE_ERROR; }; break; - case '[': if (last_structural() != ']') { log_value("starting bracket unmatched"); return TAPE_ERROR; }; break; - } - } + // Make sure the outer object or array is closed before continuing; otherwise, there are ways we + // could get into memory corruption. See https://github.com/simdjson/simdjson/issues/906 + if (!STREAMING) { + switch (*value) { + case '{': if (last_structural() != '}') { log_value("starting brace unmatched"); return TAPE_ERROR; }; break; + case '[': if (last_structural() != ']') { log_value("starting bracket unmatched"); return TAPE_ERROR; }; break; + } + } - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY(visitor.visit_empty_object(*this)); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY(visitor.visit_empty_array(*this)); break; } goto array_begin; - default: SIMDJSON_TRY(visitor.visit_root_primitive(*this, value)); break; - } - } - goto document_end; + switch (*value) { + case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; + case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; + default: SIMDJSON_TRY( visitor.visit_root_primitive(*this, value) ); break; + } + } + goto document_end; - // - // Object parser states - // - object_begin: - log_start_value("object"); - depth++; - if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } - dom_parser.is_array[depth] = false; - SIMDJSON_TRY(visitor.visit_object_start(*this)); +// +// Object parser states +// +object_begin: + log_start_value("object"); + depth++; + if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } + dom_parser.is_array[depth] = false; + SIMDJSON_TRY( visitor.visit_object_start(*this) ); - { - auto key = advance(); - if (*key != '"') { log_error("Object does not start with a key"); return TAPE_ERROR; } - SIMDJSON_TRY(visitor.increment_count(*this)); - SIMDJSON_TRY(visitor.visit_key(*this, key)); - } + { + auto key = advance(); + if (*key != '"') { log_error("Object does not start with a key"); return TAPE_ERROR; } + SIMDJSON_TRY( visitor.increment_count(*this) ); + SIMDJSON_TRY( visitor.visit_key(*this, key) ); + } - object_field: - if (simdjson_unlikely(*advance() != ':')) { log_error("Missing colon after key in object"); return TAPE_ERROR; } - { - auto value = advance(); - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY(visitor.visit_empty_object(*this)); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY(visitor.visit_empty_array(*this)); break; } goto array_begin; - default: SIMDJSON_TRY(visitor.visit_primitive(*this, value)); break; - } - } +object_field: + if (simdjson_unlikely( *advance() != ':' )) { log_error("Missing colon after key in object"); return TAPE_ERROR; } + { + auto value = advance(); + switch (*value) { + case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; + case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; + default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break; + } + } - object_continue: - switch (*advance()) { - case ',': - SIMDJSON_TRY(visitor.increment_count(*this)); - { - auto key = advance(); - if (simdjson_unlikely(*key != '"')) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; } - SIMDJSON_TRY(visitor.visit_key(*this, key)); - } - goto object_field; - case '}': log_end_value("object"); SIMDJSON_TRY(visitor.visit_object_end(*this)); goto scope_end; - default: log_error("No comma between object fields"); return TAPE_ERROR; - } +object_continue: + switch (*advance()) { + case ',': + SIMDJSON_TRY( visitor.increment_count(*this) ); + { + auto key = advance(); + if (simdjson_unlikely( *key != '"' )) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; } + SIMDJSON_TRY( visitor.visit_key(*this, key) ); + } + goto object_field; + case '}': log_end_value("object"); SIMDJSON_TRY( visitor.visit_object_end(*this) ); goto scope_end; + default: log_error("No comma between object fields"); return TAPE_ERROR; + } - scope_end: - depth--; - if (depth == 0) { goto document_end; } - if (dom_parser.is_array[depth]) { goto array_continue; } - goto object_continue; +scope_end: + depth--; + if (depth == 0) { goto document_end; } + if (dom_parser.is_array[depth]) { goto array_continue; } + goto object_continue; - // - // Array parser states - // - array_begin: - log_start_value("array"); - depth++; - if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } - dom_parser.is_array[depth] = true; - SIMDJSON_TRY(visitor.visit_array_start(*this)); - SIMDJSON_TRY(visitor.increment_count(*this)); +// +// Array parser states +// +array_begin: + log_start_value("array"); + depth++; + if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } + dom_parser.is_array[depth] = true; + SIMDJSON_TRY( visitor.visit_array_start(*this) ); + SIMDJSON_TRY( visitor.increment_count(*this) ); - array_value: - { - auto value = advance(); - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY(visitor.visit_empty_object(*this)); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY(visitor.visit_empty_array(*this)); break; } goto array_begin; - default: SIMDJSON_TRY(visitor.visit_primitive(*this, value)); break; - } - } +array_value: + { + auto value = advance(); + switch (*value) { + case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; + case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; + default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break; + } + } - array_continue: - switch (*advance()) { - case ',': SIMDJSON_TRY(visitor.increment_count(*this)); goto array_value; - case ']': log_end_value("array"); SIMDJSON_TRY(visitor.visit_array_end(*this)); goto scope_end; - default: log_error("Missing comma between array values"); return TAPE_ERROR; - } +array_continue: + switch (*advance()) { + case ',': SIMDJSON_TRY( visitor.increment_count(*this) ); goto array_value; + case ']': log_end_value("array"); SIMDJSON_TRY( visitor.visit_array_end(*this) ); goto scope_end; + default: log_error("Missing comma between array values"); return TAPE_ERROR; + } - document_end: - log_end_value("document"); - SIMDJSON_TRY(visitor.visit_document_end(*this)); +document_end: + log_end_value("document"); + SIMDJSON_TRY( visitor.visit_document_end(*this) ); - dom_parser.next_structural_index = uint32_t(next_structural - &dom_parser.structural_indexes[0]); + dom_parser.next_structural_index = uint32_t(next_structural - &dom_parser.structural_indexes[0]); - // If we didn't make it to the end, it's an error - if (!STREAMING && dom_parser.next_structural_index != dom_parser.n_structural_indexes) { - log_error("More than one JSON value at the root of the document, or extra characters at the end of the JSON!"); - return TAPE_ERROR; - } + // If we didn't make it to the end, it's an error + if ( !STREAMING && dom_parser.next_structural_index != dom_parser.n_structural_indexes ) { + log_error("More than one JSON value at the root of the document, or extra characters at the end of the JSON!"); + return TAPE_ERROR; + } - return SUCCESS; + return SUCCESS; - } // walk_document() +} // walk_document() - simdjson_inline json_iterator::json_iterator(dom_parser_implementation& _dom_parser, size_t start_structural_index) - : buf{ _dom_parser.buf }, - next_structural{ &_dom_parser.structural_indexes[start_structural_index] }, - dom_parser{ _dom_parser } { - } +simdjson_inline json_iterator::json_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index) + : buf{_dom_parser.buf}, + next_structural{&_dom_parser.structural_indexes[start_structural_index]}, + dom_parser{_dom_parser} { +} - simdjson_inline const uint8_t* json_iterator::peek() const noexcept { - return &buf[*(next_structural)]; - } - simdjson_inline const uint8_t* json_iterator::advance() noexcept { - return &buf[*(next_structural++)]; - } - simdjson_inline size_t json_iterator::remaining_len() const noexcept { - return dom_parser.len - *(next_structural - 1); - } +simdjson_inline const uint8_t *json_iterator::peek() const noexcept { + return &buf[*(next_structural)]; +} +simdjson_inline const uint8_t *json_iterator::advance() noexcept { + return &buf[*(next_structural++)]; +} +simdjson_inline size_t json_iterator::remaining_len() const noexcept { + return dom_parser.len - *(next_structural-1); +} - simdjson_inline bool json_iterator::at_eof() const noexcept { - return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; - } - simdjson_inline bool json_iterator::at_beginning() const noexcept { - return next_structural == dom_parser.structural_indexes.get(); - } - simdjson_inline uint8_t json_iterator::last_structural() const noexcept { - return buf[dom_parser.structural_indexes[dom_parser.n_structural_indexes - 1]]; - } +simdjson_inline bool json_iterator::at_eof() const noexcept { + return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; +} +simdjson_inline bool json_iterator::at_beginning() const noexcept { + return next_structural == dom_parser.structural_indexes.get(); +} +simdjson_inline uint8_t json_iterator::last_structural() const noexcept { + return buf[dom_parser.structural_indexes[dom_parser.n_structural_indexes - 1]]; +} - simdjson_inline void json_iterator::log_value(const char* type) const noexcept { - logger::log_line(*this, "", type, ""); - } +simdjson_inline void json_iterator::log_value(const char *type) const noexcept { + logger::log_line(*this, "", type, ""); +} - simdjson_inline void json_iterator::log_start_value(const char* type) const noexcept { - logger::log_line(*this, "+", type, ""); - if (logger::LOG_ENABLED) { logger::log_depth++; } - } +simdjson_inline void json_iterator::log_start_value(const char *type) const noexcept { + logger::log_line(*this, "+", type, ""); + if (logger::LOG_ENABLED) { logger::log_depth++; } +} - simdjson_inline void json_iterator::log_end_value(const char* type) const noexcept { - if (logger::LOG_ENABLED) { logger::log_depth--; } - logger::log_line(*this, "-", type, ""); - } +simdjson_inline void json_iterator::log_end_value(const char *type) const noexcept { + if (logger::LOG_ENABLED) { logger::log_depth--; } + logger::log_line(*this, "-", type, ""); +} - simdjson_inline void json_iterator::log_error(const char* error) const noexcept { - logger::log_line(*this, "", "ERROR", error); - } +simdjson_inline void json_iterator::log_error(const char *error) const noexcept { + logger::log_line(*this, "", "ERROR", error); +} - template - simdjson_warn_unused simdjson_inline error_code json_iterator::visit_root_primitive(V& visitor, const uint8_t* value) noexcept { - switch (*value) { - case '"': return visitor.visit_root_string(*this, value); - case 't': return visitor.visit_root_true_atom(*this, value); - case 'f': return visitor.visit_root_false_atom(*this, value); - case 'n': return visitor.visit_root_null_atom(*this, value); - case '-': - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - return visitor.visit_root_number(*this, value); - default: - log_error("Document starts with a non-value character"); - return TAPE_ERROR; - } - } - template - simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V& visitor, const uint8_t* value) noexcept { - // Use the fact that most scalars are going to be either strings or numbers. - if (*value == '"') { - return visitor.visit_string(*this, value); - } - else if (((*value - '0') < 10) || (*value == '-')) { - return visitor.visit_number(*this, value); - } - // true, false, null are uncommon. - switch (*value) { - case 't': return visitor.visit_true_atom(*this, value); - case 'f': return visitor.visit_false_atom(*this, value); - case 'n': return visitor.visit_null_atom(*this, value); - default: - log_error("Non-value found when value was expected!"); - return TAPE_ERROR; - } - } +template +simdjson_warn_unused simdjson_inline error_code json_iterator::visit_root_primitive(V &visitor, const uint8_t *value) noexcept { + switch (*value) { + case '"': return visitor.visit_root_string(*this, value); + case 't': return visitor.visit_root_true_atom(*this, value); + case 'f': return visitor.visit_root_false_atom(*this, value); + case 'n': return visitor.visit_root_null_atom(*this, value); + case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return visitor.visit_root_number(*this, value); + default: + log_error("Document starts with a non-value character"); + return TAPE_ERROR; + } +} +template +simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V &visitor, const uint8_t *value) noexcept { + // Use the fact that most scalars are going to be either strings or numbers. + if(*value == '"') { + return visitor.visit_string(*this, value); + } else if (((*value - '0') < 10) || (*value == '-')) { + return visitor.visit_number(*this, value); + } + // true, false, null are uncommon. + switch (*value) { + case 't': return visitor.visit_true_atom(*this, value); + case 'f': return visitor.visit_false_atom(*this, value); + case 'n': return visitor.visit_null_atom(*this, value); + default: + log_error("Non-value found when value was expected!"); + return TAPE_ERROR; + } +} - } // namespace stage2 - } // unnamed namespace - } // namespace fallback +} // namespace stage2 +} // unnamed namespace +} // namespace fallback } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_JSON_ITERATOR_H @@ -55451,109 +64128,109 @@ namespace simdjson { #include namespace simdjson { - namespace fallback { - namespace { - namespace stage2 { +namespace fallback { +namespace { +namespace stage2 { - struct tape_writer { - /** The next place to write to tape */ - uint64_t* next_tape_loc; +struct tape_writer { + /** The next place to write to tape */ + uint64_t *next_tape_loc; - /** Write a signed 64-bit value to tape. */ - simdjson_inline void append_s64(int64_t value) noexcept; + /** Write a signed 64-bit value to tape. */ + simdjson_inline void append_s64(int64_t value) noexcept; - /** Write an unsigned 64-bit value to tape. */ - simdjson_inline void append_u64(uint64_t value) noexcept; + /** Write an unsigned 64-bit value to tape. */ + simdjson_inline void append_u64(uint64_t value) noexcept; - /** Write a double value to tape. */ - simdjson_inline void append_double(double value) noexcept; + /** Write a double value to tape. */ + simdjson_inline void append_double(double value) noexcept; - /** - * Append a tape entry (an 8-bit type,and 56 bits worth of value). - */ - simdjson_inline void append(uint64_t val, internal::tape_type t) noexcept; + /** + * Append a tape entry (an 8-bit type,and 56 bits worth of value). + */ + simdjson_inline void append(uint64_t val, internal::tape_type t) noexcept; - /** - * Skip the current tape entry without writing. - * - * Used to skip the start of the container, since we'll come back later to fill it in when the - * container ends. - */ - simdjson_inline void skip() noexcept; + /** + * Skip the current tape entry without writing. + * + * Used to skip the start of the container, since we'll come back later to fill it in when the + * container ends. + */ + simdjson_inline void skip() noexcept; - /** - * Skip the number of tape entries necessary to write a large u64 or i64. - */ - simdjson_inline void skip_large_integer() noexcept; + /** + * Skip the number of tape entries necessary to write a large u64 or i64. + */ + simdjson_inline void skip_large_integer() noexcept; - /** - * Skip the number of tape entries necessary to write a double. - */ - simdjson_inline void skip_double() noexcept; + /** + * Skip the number of tape entries necessary to write a double. + */ + simdjson_inline void skip_double() noexcept; - /** - * Write a value to a known location on tape. - * - * Used to go back and write out the start of a container after the container ends. - */ - simdjson_inline static void write(uint64_t& tape_loc, uint64_t val, internal::tape_type t) noexcept; + /** + * Write a value to a known location on tape. + * + * Used to go back and write out the start of a container after the container ends. + */ + simdjson_inline static void write(uint64_t &tape_loc, uint64_t val, internal::tape_type t) noexcept; - private: - /** - * Append both the tape entry, and a supplementary value following it. Used for types that need - * all 64 bits, such as double and uint64_t. - */ - template - simdjson_inline void append2(uint64_t val, T val2, internal::tape_type t) noexcept; - }; // struct tape_writer +private: + /** + * Append both the tape entry, and a supplementary value following it. Used for types that need + * all 64 bits, such as double and uint64_t. + */ + template + simdjson_inline void append2(uint64_t val, T val2, internal::tape_type t) noexcept; +}; // struct tape_writer - simdjson_inline void tape_writer::append_s64(int64_t value) noexcept { - append2(0, value, internal::tape_type::INT64); - } +simdjson_inline void tape_writer::append_s64(int64_t value) noexcept { + append2(0, value, internal::tape_type::INT64); +} - simdjson_inline void tape_writer::append_u64(uint64_t value) noexcept { - append(0, internal::tape_type::UINT64); - *next_tape_loc = value; - next_tape_loc++; - } +simdjson_inline void tape_writer::append_u64(uint64_t value) noexcept { + append(0, internal::tape_type::UINT64); + *next_tape_loc = value; + next_tape_loc++; +} - /** Write a double value to tape. */ - simdjson_inline void tape_writer::append_double(double value) noexcept { - append2(0, value, internal::tape_type::DOUBLE); - } +/** Write a double value to tape. */ +simdjson_inline void tape_writer::append_double(double value) noexcept { + append2(0, value, internal::tape_type::DOUBLE); +} - simdjson_inline void tape_writer::skip() noexcept { - next_tape_loc++; - } +simdjson_inline void tape_writer::skip() noexcept { + next_tape_loc++; +} - simdjson_inline void tape_writer::skip_large_integer() noexcept { - next_tape_loc += 2; - } +simdjson_inline void tape_writer::skip_large_integer() noexcept { + next_tape_loc += 2; +} - simdjson_inline void tape_writer::skip_double() noexcept { - next_tape_loc += 2; - } +simdjson_inline void tape_writer::skip_double() noexcept { + next_tape_loc += 2; +} - simdjson_inline void tape_writer::append(uint64_t val, internal::tape_type t) noexcept { - *next_tape_loc = val | ((uint64_t(char(t))) << 56); - next_tape_loc++; - } +simdjson_inline void tape_writer::append(uint64_t val, internal::tape_type t) noexcept { + *next_tape_loc = val | ((uint64_t(char(t))) << 56); + next_tape_loc++; +} - template - simdjson_inline void tape_writer::append2(uint64_t val, T val2, internal::tape_type t) noexcept { - append(val, t); - static_assert(sizeof(val2) == sizeof(*next_tape_loc), "Type is not 64 bits!"); - memcpy(next_tape_loc, &val2, sizeof(val2)); - next_tape_loc++; - } +template +simdjson_inline void tape_writer::append2(uint64_t val, T val2, internal::tape_type t) noexcept { + append(val, t); + static_assert(sizeof(val2) == sizeof(*next_tape_loc), "Type is not 64 bits!"); + memcpy(next_tape_loc, &val2, sizeof(val2)); + next_tape_loc++; +} - simdjson_inline void tape_writer::write(uint64_t& tape_loc, uint64_t val, internal::tape_type t) noexcept { - tape_loc = val | ((uint64_t(char(t))) << 56); - } +simdjson_inline void tape_writer::write(uint64_t &tape_loc, uint64_t val, internal::tape_type t) noexcept { + tape_loc = val | ((uint64_t(char(t))) << 56); +} - } // namespace stage2 - } // unnamed namespace - } // namespace fallback +} // namespace stage2 +} // unnamed namespace +} // namespace fallback } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_WRITER_H @@ -55576,284 +64253,284 @@ namespace simdjson { namespace simdjson { - namespace fallback { - namespace { - namespace stage2 { +namespace fallback { +namespace { +namespace stage2 { - struct tape_builder { - template - simdjson_warn_unused static simdjson_inline error_code parse_document( - dom_parser_implementation& dom_parser, - dom::document& doc) noexcept; +struct tape_builder { + template + simdjson_warn_unused static simdjson_inline error_code parse_document( + dom_parser_implementation &dom_parser, + dom::document &doc) noexcept; - /** Called when a non-empty document starts. */ - simdjson_warn_unused simdjson_inline error_code visit_document_start(json_iterator& iter) noexcept; - /** Called when a non-empty document ends without error. */ - simdjson_warn_unused simdjson_inline error_code visit_document_end(json_iterator& iter) noexcept; + /** Called when a non-empty document starts. */ + simdjson_warn_unused simdjson_inline error_code visit_document_start(json_iterator &iter) noexcept; + /** Called when a non-empty document ends without error. */ + simdjson_warn_unused simdjson_inline error_code visit_document_end(json_iterator &iter) noexcept; - /** Called when a non-empty array starts. */ - simdjson_warn_unused simdjson_inline error_code visit_array_start(json_iterator& iter) noexcept; - /** Called when a non-empty array ends. */ - simdjson_warn_unused simdjson_inline error_code visit_array_end(json_iterator& iter) noexcept; - /** Called when an empty array is found. */ - simdjson_warn_unused simdjson_inline error_code visit_empty_array(json_iterator& iter) noexcept; + /** Called when a non-empty array starts. */ + simdjson_warn_unused simdjson_inline error_code visit_array_start(json_iterator &iter) noexcept; + /** Called when a non-empty array ends. */ + simdjson_warn_unused simdjson_inline error_code visit_array_end(json_iterator &iter) noexcept; + /** Called when an empty array is found. */ + simdjson_warn_unused simdjson_inline error_code visit_empty_array(json_iterator &iter) noexcept; - /** Called when a non-empty object starts. */ - simdjson_warn_unused simdjson_inline error_code visit_object_start(json_iterator& iter) noexcept; - /** - * Called when a key in a field is encountered. - * - * primitive, visit_object_start, visit_empty_object, visit_array_start, or visit_empty_array - * will be called after this with the field value. - */ - simdjson_warn_unused simdjson_inline error_code visit_key(json_iterator& iter, const uint8_t* key) noexcept; - /** Called when a non-empty object ends. */ - simdjson_warn_unused simdjson_inline error_code visit_object_end(json_iterator& iter) noexcept; - /** Called when an empty object is found. */ - simdjson_warn_unused simdjson_inline error_code visit_empty_object(json_iterator& iter) noexcept; + /** Called when a non-empty object starts. */ + simdjson_warn_unused simdjson_inline error_code visit_object_start(json_iterator &iter) noexcept; + /** + * Called when a key in a field is encountered. + * + * primitive, visit_object_start, visit_empty_object, visit_array_start, or visit_empty_array + * will be called after this with the field value. + */ + simdjson_warn_unused simdjson_inline error_code visit_key(json_iterator &iter, const uint8_t *key) noexcept; + /** Called when a non-empty object ends. */ + simdjson_warn_unused simdjson_inline error_code visit_object_end(json_iterator &iter) noexcept; + /** Called when an empty object is found. */ + simdjson_warn_unused simdjson_inline error_code visit_empty_object(json_iterator &iter) noexcept; - /** - * Called when a string, number, boolean or null is found. - */ - simdjson_warn_unused simdjson_inline error_code visit_primitive(json_iterator& iter, const uint8_t* value) noexcept; - /** - * Called when a string, number, boolean or null is found at the top level of a document (i.e. - * when there is no array or object and the entire document is a single string, number, boolean or - * null. - * - * This is separate from primitive() because simdjson's normal primitive parsing routines assume - * there is at least one more token after the value, which is only true in an array or object. - */ - simdjson_warn_unused simdjson_inline error_code visit_root_primitive(json_iterator& iter, const uint8_t* value) noexcept; + /** + * Called when a string, number, boolean or null is found. + */ + simdjson_warn_unused simdjson_inline error_code visit_primitive(json_iterator &iter, const uint8_t *value) noexcept; + /** + * Called when a string, number, boolean or null is found at the top level of a document (i.e. + * when there is no array or object and the entire document is a single string, number, boolean or + * null. + * + * This is separate from primitive() because simdjson's normal primitive parsing routines assume + * there is at least one more token after the value, which is only true in an array or object. + */ + simdjson_warn_unused simdjson_inline error_code visit_root_primitive(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_string(json_iterator& iter, const uint8_t* value, bool key = false) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_number(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_true_atom(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_false_atom(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_null_atom(json_iterator& iter, const uint8_t* value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_string(json_iterator &iter, const uint8_t *value, bool key = false) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_number(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_true_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_false_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_null_atom(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_string(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_number(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_true_atom(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_false_atom(json_iterator& iter, const uint8_t* value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_null_atom(json_iterator& iter, const uint8_t* value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_string(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_number(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_true_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept; + simdjson_warn_unused simdjson_inline error_code visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept; - /** Called each time a new field or element in an array or object is found. */ - simdjson_warn_unused simdjson_inline error_code increment_count(json_iterator& iter) noexcept; + /** Called each time a new field or element in an array or object is found. */ + simdjson_warn_unused simdjson_inline error_code increment_count(json_iterator &iter) noexcept; - /** Next location to write to tape */ - tape_writer tape; - private: - /** Next write location in the string buf for stage 2 parsing */ - uint8_t* current_string_buf_loc; + /** Next location to write to tape */ + tape_writer tape; +private: + /** Next write location in the string buf for stage 2 parsing */ + uint8_t *current_string_buf_loc; - simdjson_inline tape_builder(dom::document& doc) noexcept; + simdjson_inline tape_builder(dom::document &doc) noexcept; - simdjson_inline uint32_t next_tape_index(json_iterator& iter) const noexcept; - simdjson_inline void start_container(json_iterator& iter) noexcept; - simdjson_warn_unused simdjson_inline error_code end_container(json_iterator& iter, internal::tape_type start, internal::tape_type end) noexcept; - simdjson_warn_unused simdjson_inline error_code empty_container(json_iterator& iter, internal::tape_type start, internal::tape_type end) noexcept; - simdjson_inline uint8_t* on_start_string(json_iterator& iter) noexcept; - simdjson_inline void on_end_string(uint8_t* dst) noexcept; - }; // struct tape_builder + simdjson_inline uint32_t next_tape_index(json_iterator &iter) const noexcept; + simdjson_inline void start_container(json_iterator &iter) noexcept; + simdjson_warn_unused simdjson_inline error_code end_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept; + simdjson_warn_unused simdjson_inline error_code empty_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept; + simdjson_inline uint8_t *on_start_string(json_iterator &iter) noexcept; + simdjson_inline void on_end_string(uint8_t *dst) noexcept; +}; // struct tape_builder - template - simdjson_warn_unused simdjson_inline error_code tape_builder::parse_document( - dom_parser_implementation& dom_parser, - dom::document& doc) noexcept { - dom_parser.doc = &doc; - json_iterator iter(dom_parser, STREAMING ? dom_parser.next_structural_index : 0); - tape_builder builder(doc); - return iter.walk_document(builder); - } +template +simdjson_warn_unused simdjson_inline error_code tape_builder::parse_document( + dom_parser_implementation &dom_parser, + dom::document &doc) noexcept { + dom_parser.doc = &doc; + json_iterator iter(dom_parser, STREAMING ? dom_parser.next_structural_index : 0); + tape_builder builder(doc); + return iter.walk_document(builder); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_primitive(json_iterator& iter, const uint8_t* value) noexcept { - return iter.visit_root_primitive(*this, value); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_primitive(json_iterator& iter, const uint8_t* value) noexcept { - return iter.visit_primitive(*this, value); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_object(json_iterator& iter) noexcept { - return empty_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_array(json_iterator& iter) noexcept { - return empty_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_primitive(json_iterator &iter, const uint8_t *value) noexcept { + return iter.visit_root_primitive(*this, value); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_primitive(json_iterator &iter, const uint8_t *value) noexcept { + return iter.visit_primitive(*this, value); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_object(json_iterator &iter) noexcept { + return empty_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_array(json_iterator &iter) noexcept { + return empty_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_start(json_iterator& iter) noexcept { - start_container(iter); - return SUCCESS; - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_start(json_iterator& iter) noexcept { - start_container(iter); - return SUCCESS; - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_start(json_iterator& iter) noexcept { - start_container(iter); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_start(json_iterator &iter) noexcept { + start_container(iter); + return SUCCESS; +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_start(json_iterator &iter) noexcept { + start_container(iter); + return SUCCESS; +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_start(json_iterator &iter) noexcept { + start_container(iter); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_end(json_iterator& iter) noexcept { - return end_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_end(json_iterator& iter) noexcept { - return end_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_end(json_iterator& iter) noexcept { - constexpr uint32_t start_tape_index = 0; - tape.append(start_tape_index, internal::tape_type::ROOT); - tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter), internal::tape_type::ROOT); - return SUCCESS; - } - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_key(json_iterator& iter, const uint8_t* key) noexcept { - return visit_string(iter, key, true); - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_end(json_iterator &iter) noexcept { + return end_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_end(json_iterator &iter) noexcept { + return end_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_end(json_iterator &iter) noexcept { + constexpr uint32_t start_tape_index = 0; + tape.append(start_tape_index, internal::tape_type::ROOT); + tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter), internal::tape_type::ROOT); + return SUCCESS; +} +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_key(json_iterator &iter, const uint8_t *key) noexcept { + return visit_string(iter, key, true); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::increment_count(json_iterator& iter) noexcept { - iter.dom_parser.open_containers[iter.depth].count++; // we have a key value pair in the object at parser.dom_parser.depth - 1 - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::increment_count(json_iterator &iter) noexcept { + iter.dom_parser.open_containers[iter.depth].count++; // we have a key value pair in the object at parser.dom_parser.depth - 1 + return SUCCESS; +} - simdjson_inline tape_builder::tape_builder(dom::document& doc) noexcept : tape{ doc.tape.get() }, current_string_buf_loc{ doc.string_buf.get() } {} +simdjson_inline tape_builder::tape_builder(dom::document &doc) noexcept : tape{doc.tape.get()}, current_string_buf_loc{doc.string_buf.get()} {} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_string(json_iterator& iter, const uint8_t* value, bool key) noexcept { - iter.log_value(key ? "key" : "string"); - uint8_t* dst = on_start_string(iter); - dst = stringparsing::parse_string(value + 1, dst, false); // We do not allow replacement when the escape characters are invalid. - if (dst == nullptr) { - iter.log_error("Invalid escape in string"); - return STRING_ERROR; - } - on_end_string(dst); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_string(json_iterator &iter, const uint8_t *value, bool key) noexcept { + iter.log_value(key ? "key" : "string"); + uint8_t *dst = on_start_string(iter); + dst = stringparsing::parse_string(value+1, dst, false); // We do not allow replacement when the escape characters are invalid. + if (dst == nullptr) { + iter.log_error("Invalid escape in string"); + return STRING_ERROR; + } + on_end_string(dst); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_string(json_iterator& iter, const uint8_t* value) noexcept { - return visit_string(iter, value); - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_string(json_iterator &iter, const uint8_t *value) noexcept { + return visit_string(iter, value); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_number(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("number"); - return numberparsing::parse_number(value, tape); - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_number(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("number"); + return numberparsing::parse_number(value, tape); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_number(json_iterator& iter, const uint8_t* value) noexcept { - // - // We need to make a copy to make sure that the string is space terminated. - // This is not about padding the input, which should already padded up - // to len + SIMDJSON_PADDING. However, we have no control at this stage - // on how the padding was done. What if the input string was padded with nulls? - // It is quite common for an input string to have an extra null character (C string). - // We do not want to allow 9\0 (where \0 is the null character) inside a JSON - // document, but the string "9\0" by itself is fine. So we make a copy and - // pad the input with spaces when we know that there is just one input element. - // This copy is relatively expensive, but it will almost never be called in - // practice unless you are in the strange scenario where you have many JSON - // documents made of single atoms. - // - std::unique_ptrcopy(new (std::nothrow) uint8_t[iter.remaining_len() + SIMDJSON_PADDING]); - if (copy.get() == nullptr) { return MEMALLOC; } - std::memcpy(copy.get(), value, iter.remaining_len()); - std::memset(copy.get() + iter.remaining_len(), ' ', SIMDJSON_PADDING); - error_code error = visit_number(iter, copy.get()); - return error; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_number(json_iterator &iter, const uint8_t *value) noexcept { + // + // We need to make a copy to make sure that the string is space terminated. + // This is not about padding the input, which should already padded up + // to len + SIMDJSON_PADDING. However, we have no control at this stage + // on how the padding was done. What if the input string was padded with nulls? + // It is quite common for an input string to have an extra null character (C string). + // We do not want to allow 9\0 (where \0 is the null character) inside a JSON + // document, but the string "9\0" by itself is fine. So we make a copy and + // pad the input with spaces when we know that there is just one input element. + // This copy is relatively expensive, but it will almost never be called in + // practice unless you are in the strange scenario where you have many JSON + // documents made of single atoms. + // + std::unique_ptrcopy(new (std::nothrow) uint8_t[iter.remaining_len() + SIMDJSON_PADDING]); + if (copy.get() == nullptr) { return MEMALLOC; } + std::memcpy(copy.get(), value, iter.remaining_len()); + std::memset(copy.get() + iter.remaining_len(), ' ', SIMDJSON_PADDING); + error_code error = visit_number(iter, copy.get()); + return error; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_true_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("true"); - if (!atomparsing::is_valid_true_atom(value)) { return T_ATOM_ERROR; } - tape.append(0, internal::tape_type::TRUE_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_true_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("true"); + if (!atomparsing::is_valid_true_atom(value)) { return T_ATOM_ERROR; } + tape.append(0, internal::tape_type::TRUE_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_true_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("true"); - if (!atomparsing::is_valid_true_atom(value, iter.remaining_len())) { return T_ATOM_ERROR; } - tape.append(0, internal::tape_type::TRUE_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_true_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("true"); + if (!atomparsing::is_valid_true_atom(value, iter.remaining_len())) { return T_ATOM_ERROR; } + tape.append(0, internal::tape_type::TRUE_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_false_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("false"); - if (!atomparsing::is_valid_false_atom(value)) { return F_ATOM_ERROR; } - tape.append(0, internal::tape_type::FALSE_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_false_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("false"); + if (!atomparsing::is_valid_false_atom(value)) { return F_ATOM_ERROR; } + tape.append(0, internal::tape_type::FALSE_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_false_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("false"); - if (!atomparsing::is_valid_false_atom(value, iter.remaining_len())) { return F_ATOM_ERROR; } - tape.append(0, internal::tape_type::FALSE_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("false"); + if (!atomparsing::is_valid_false_atom(value, iter.remaining_len())) { return F_ATOM_ERROR; } + tape.append(0, internal::tape_type::FALSE_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_null_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("null"); - if (!atomparsing::is_valid_null_atom(value)) { return N_ATOM_ERROR; } - tape.append(0, internal::tape_type::NULL_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_null_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("null"); + if (!atomparsing::is_valid_null_atom(value)) { return N_ATOM_ERROR; } + tape.append(0, internal::tape_type::NULL_VALUE); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_null_atom(json_iterator& iter, const uint8_t* value) noexcept { - iter.log_value("null"); - if (!atomparsing::is_valid_null_atom(value, iter.remaining_len())) { return N_ATOM_ERROR; } - tape.append(0, internal::tape_type::NULL_VALUE); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept { + iter.log_value("null"); + if (!atomparsing::is_valid_null_atom(value, iter.remaining_len())) { return N_ATOM_ERROR; } + tape.append(0, internal::tape_type::NULL_VALUE); + return SUCCESS; +} - // private: +// private: - simdjson_inline uint32_t tape_builder::next_tape_index(json_iterator& iter) const noexcept { - return uint32_t(tape.next_tape_loc - iter.dom_parser.doc->tape.get()); - } +simdjson_inline uint32_t tape_builder::next_tape_index(json_iterator &iter) const noexcept { + return uint32_t(tape.next_tape_loc - iter.dom_parser.doc->tape.get()); +} - simdjson_warn_unused simdjson_inline error_code tape_builder::empty_container(json_iterator& iter, internal::tape_type start, internal::tape_type end) noexcept { - auto start_index = next_tape_index(iter); - tape.append(start_index + 2, start); - tape.append(start_index, end); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::empty_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept { + auto start_index = next_tape_index(iter); + tape.append(start_index+2, start); + tape.append(start_index, end); + return SUCCESS; +} - simdjson_inline void tape_builder::start_container(json_iterator& iter) noexcept { - iter.dom_parser.open_containers[iter.depth].tape_index = next_tape_index(iter); - iter.dom_parser.open_containers[iter.depth].count = 0; - tape.skip(); // We don't actually *write* the start element until the end. - } +simdjson_inline void tape_builder::start_container(json_iterator &iter) noexcept { + iter.dom_parser.open_containers[iter.depth].tape_index = next_tape_index(iter); + iter.dom_parser.open_containers[iter.depth].count = 0; + tape.skip(); // We don't actually *write* the start element until the end. +} - simdjson_warn_unused simdjson_inline error_code tape_builder::end_container(json_iterator& iter, internal::tape_type start, internal::tape_type end) noexcept { - // Write the ending tape element, pointing at the start location - const uint32_t start_tape_index = iter.dom_parser.open_containers[iter.depth].tape_index; - tape.append(start_tape_index, end); - // Write the start tape element, pointing at the end location (and including count) - // count can overflow if it exceeds 24 bits... so we saturate - // the convention being that a cnt of 0xffffff or more is undetermined in value (>= 0xffffff). - const uint32_t count = iter.dom_parser.open_containers[iter.depth].count; - const uint32_t cntsat = count > 0xFFFFFF ? 0xFFFFFF : count; - tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter) | (uint64_t(cntsat) << 32), start); - return SUCCESS; - } +simdjson_warn_unused simdjson_inline error_code tape_builder::end_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept { + // Write the ending tape element, pointing at the start location + const uint32_t start_tape_index = iter.dom_parser.open_containers[iter.depth].tape_index; + tape.append(start_tape_index, end); + // Write the start tape element, pointing at the end location (and including count) + // count can overflow if it exceeds 24 bits... so we saturate + // the convention being that a cnt of 0xffffff or more is undetermined in value (>= 0xffffff). + const uint32_t count = iter.dom_parser.open_containers[iter.depth].count; + const uint32_t cntsat = count > 0xFFFFFF ? 0xFFFFFF : count; + tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter) | (uint64_t(cntsat) << 32), start); + return SUCCESS; +} - simdjson_inline uint8_t* tape_builder::on_start_string(json_iterator& iter) noexcept { - // we advance the point, accounting for the fact that we have a NULL termination - tape.append(current_string_buf_loc - iter.dom_parser.doc->string_buf.get(), internal::tape_type::STRING); - return current_string_buf_loc + sizeof(uint32_t); - } +simdjson_inline uint8_t *tape_builder::on_start_string(json_iterator &iter) noexcept { + // we advance the point, accounting for the fact that we have a NULL termination + tape.append(current_string_buf_loc - iter.dom_parser.doc->string_buf.get(), internal::tape_type::STRING); + return current_string_buf_loc + sizeof(uint32_t); +} - simdjson_inline void tape_builder::on_end_string(uint8_t* dst) noexcept { - uint32_t str_length = uint32_t(dst - (current_string_buf_loc + sizeof(uint32_t))); - // TODO check for overflow in case someone has a crazy string (>=4GB?) - // But only add the overflow check when the document itself exceeds 4GB - // Currently unneeded because we refuse to parse docs larger or equal to 4GB. - memcpy(current_string_buf_loc, &str_length, sizeof(uint32_t)); - // NULL termination is still handy if you expect all your strings to - // be NULL terminated? It comes at a small cost - *dst = 0; - current_string_buf_loc = dst + 1; - } +simdjson_inline void tape_builder::on_end_string(uint8_t *dst) noexcept { + uint32_t str_length = uint32_t(dst - (current_string_buf_loc + sizeof(uint32_t))); + // TODO check for overflow in case someone has a crazy string (>=4GB?) + // But only add the overflow check when the document itself exceeds 4GB + // Currently unneeded because we refuse to parse docs larger or equal to 4GB. + memcpy(current_string_buf_loc, &str_length, sizeof(uint32_t)); + // NULL termination is still handy if you expect all your strings to + // be NULL terminated? It comes at a small cost + *dst = 0; + current_string_buf_loc = dst + 1; +} - } // namespace stage2 - } // unnamed namespace - } // namespace fallback +} // namespace stage2 +} // unnamed namespace +} // namespace fallback } // namespace simdjson #endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_BUILDER_H @@ -55864,365 +64541,412 @@ namespace simdjson { // namespace simdjson { - namespace fallback { +namespace fallback { - simdjson_warn_unused error_code implementation::create_dom_parser_implementation( - size_t capacity, - size_t max_depth, - std::unique_ptr& dst - ) const noexcept { - dst.reset(new (std::nothrow) fallback::dom_parser_implementation()); - if (!dst) { return MEMALLOC; } - if (auto err = dst->set_capacity(capacity)) - return err; - if (auto err = dst->set_max_depth(max_depth)) - return err; - return SUCCESS; - } +simdjson_warn_unused error_code implementation::create_dom_parser_implementation( + size_t capacity, + size_t max_depth, + std::unique_ptr& dst +) const noexcept { + dst.reset( new (std::nothrow) fallback::dom_parser_implementation() ); + if (!dst) { return MEMALLOC; } + if (auto err = dst->set_capacity(capacity)) + return err; + if (auto err = dst->set_max_depth(max_depth)) + return err; + return SUCCESS; +} - namespace { - namespace stage1 { +namespace { +namespace stage1 { - class structural_scanner { - public: +class structural_scanner { +public: - simdjson_inline structural_scanner(dom_parser_implementation& _parser, stage1_mode _partial) - : buf{ _parser.buf }, - next_structural_index{ _parser.structural_indexes.get() }, - parser{ _parser }, - len{ static_cast(_parser.len) }, - partial{ _partial } { - } +simdjson_inline structural_scanner(dom_parser_implementation &_parser, stage1_mode _partial) + : buf{_parser.buf}, + next_structural_index{_parser.structural_indexes.get()}, + parser{_parser}, + len{static_cast(_parser.len)}, + partial{_partial} { +} - simdjson_inline void add_structural() { - *next_structural_index = idx; - next_structural_index++; - } +simdjson_inline void add_structural() { + *next_structural_index = idx; + next_structural_index++; +} - simdjson_inline bool is_continuation(uint8_t c) { - return (c & 0xc0) == 0x80; - } +simdjson_inline bool is_continuation(uint8_t c) { + return (c & 0xc0) == 0x80; +} - simdjson_inline void validate_utf8_character() { - // Continuation - if (simdjson_unlikely((buf[idx] & 0x40) == 0)) { - // extra continuation - error = UTF8_ERROR; - idx++; - return; - } +simdjson_inline void validate_utf8_character() { + // Continuation + if (simdjson_unlikely((buf[idx] & 0x40) == 0)) { + // extra continuation + error = UTF8_ERROR; + idx++; + return; + } - // 2-byte - if ((buf[idx] & 0x20) == 0) { - // missing continuation - if (simdjson_unlikely(idx + 1 > len || !is_continuation(buf[idx + 1]))) { - if (idx + 1 > len && is_streaming(partial)) { idx = len; return; } - error = UTF8_ERROR; - idx++; - return; - } - // overlong: 1100000_ 10______ - if (buf[idx] <= 0xc1) { error = UTF8_ERROR; } - idx += 2; - return; - } + // 2-byte + if ((buf[idx] & 0x20) == 0) { + // missing continuation + if (simdjson_unlikely(idx+1 > len || !is_continuation(buf[idx+1]))) { + if (idx+1 > len && is_streaming(partial)) { idx = len; return; } + error = UTF8_ERROR; + idx++; + return; + } + // overlong: 1100000_ 10______ + if (buf[idx] <= 0xc1) { error = UTF8_ERROR; } + idx += 2; + return; + } - // 3-byte - if ((buf[idx] & 0x10) == 0) { - // missing continuation - if (simdjson_unlikely(idx + 2 > len || !is_continuation(buf[idx + 1]) || !is_continuation(buf[idx + 2]))) { - if (idx + 2 > len && is_streaming(partial)) { idx = len; return; } - error = UTF8_ERROR; - idx++; - return; - } - // overlong: 11100000 100_____ ________ - if (buf[idx] == 0xe0 && buf[idx + 1] <= 0x9f) { error = UTF8_ERROR; } - // surrogates: U+D800-U+DFFF 11101101 101_____ - if (buf[idx] == 0xed && buf[idx + 1] >= 0xa0) { error = UTF8_ERROR; } - idx += 3; - return; - } + // 3-byte + if ((buf[idx] & 0x10) == 0) { + // missing continuation + if (simdjson_unlikely(idx+2 > len || !is_continuation(buf[idx+1]) || !is_continuation(buf[idx+2]))) { + if (idx+2 > len && is_streaming(partial)) { idx = len; return; } + error = UTF8_ERROR; + idx++; + return; + } + // overlong: 11100000 100_____ ________ + if (buf[idx] == 0xe0 && buf[idx+1] <= 0x9f) { error = UTF8_ERROR; } + // surrogates: U+D800-U+DFFF 11101101 101_____ + if (buf[idx] == 0xed && buf[idx+1] >= 0xa0) { error = UTF8_ERROR; } + idx += 3; + return; + } - // 4-byte - // missing continuation - if (simdjson_unlikely(idx + 3 > len || !is_continuation(buf[idx + 1]) || !is_continuation(buf[idx + 2]) || !is_continuation(buf[idx + 3]))) { - if (idx + 2 > len && is_streaming(partial)) { idx = len; return; } - error = UTF8_ERROR; - idx++; - return; - } - // overlong: 11110000 1000____ ________ ________ - if (buf[idx] == 0xf0 && buf[idx + 1] <= 0x8f) { error = UTF8_ERROR; } - // too large: > U+10FFFF: - // 11110100 (1001|101_)____ - // 1111(1___|011_|0101) 10______ - // also includes 5, 6, 7 and 8 byte characters: - // 11111___ - if (buf[idx] == 0xf4 && buf[idx + 1] >= 0x90) { error = UTF8_ERROR; } - if (buf[idx] >= 0xf5) { error = UTF8_ERROR; } - idx += 4; - } + // 4-byte + // missing continuation + if (simdjson_unlikely(idx+3 > len || !is_continuation(buf[idx+1]) || !is_continuation(buf[idx+2]) || !is_continuation(buf[idx+3]))) { + if (idx+2 > len && is_streaming(partial)) { idx = len; return; } + error = UTF8_ERROR; + idx++; + return; + } + // overlong: 11110000 1000____ ________ ________ + if (buf[idx] == 0xf0 && buf[idx+1] <= 0x8f) { error = UTF8_ERROR; } + // too large: > U+10FFFF: + // 11110100 (1001|101_)____ + // 1111(1___|011_|0101) 10______ + // also includes 5, 6, 7 and 8 byte characters: + // 11111___ + if (buf[idx] == 0xf4 && buf[idx+1] >= 0x90) { error = UTF8_ERROR; } + if (buf[idx] >= 0xf5) { error = UTF8_ERROR; } + idx += 4; +} - // Returns true if the string is unclosed. - simdjson_inline bool validate_string() { - idx++; // skip first quote - while (idx < len && buf[idx] != '"') { - if (buf[idx] == '\\') { - idx += 2; - } - else if (simdjson_unlikely(buf[idx] & 0x80)) { - validate_utf8_character(); - } - else { - if (buf[idx] < 0x20) { error = UNESCAPED_CHARS; } - idx++; - } - } - if (idx >= len) { return true; } - return false; - } +static const uint8_t CHAR_TYPE_SPACE = 1 << 0; +static const uint8_t CHAR_TYPE_OPERATOR = 1 << 1; +static const uint8_t CHAR_TYPE_ESC_ASCII = 1 << 2; +static const uint8_t CHAR_TYPE_NON_ASCII = 1 << 3; - simdjson_inline bool is_whitespace_or_operator(uint8_t c) { - switch (c) { - case '{': case '}': case '[': case ']': case ',': case ':': - case ' ': case '\r': case '\n': case '\t': - return true; - default: - return false; - } - } +const uint8_t char_table[256] = { + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x04, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 +}; - // - // Parse the entire input in STEP_SIZE-byte chunks. - // - simdjson_inline error_code scan() { - bool unclosed_string = false; - for (; idx < len; idx++) { - switch (buf[idx]) { - // String - case '"': - add_structural(); - unclosed_string |= validate_string(); - break; - // Operator - case '{': case '}': case '[': case ']': case ',': case ':': - add_structural(); - break; - // Whitespace - case ' ': case '\r': case '\n': case '\t': - break; - // Primitive or invalid character (invalid characters will be checked in stage 2) - default: - // Anything else, add the structural and go until we find the next one - add_structural(); - while (idx + 1 < len && !is_whitespace_or_operator(buf[idx + 1])) { - idx++; - }; - break; - } - } - // We pad beyond. - // https://github.com/simdjson/simdjson/issues/906 - // See json_structural_indexer.h for an explanation. - *next_structural_index = len; // assumed later in partial == stage1_mode::streaming_final - next_structural_index[1] = len; - next_structural_index[2] = 0; - parser.n_structural_indexes = uint32_t(next_structural_index - parser.structural_indexes.get()); - if (simdjson_unlikely(parser.n_structural_indexes == 0)) { return EMPTY; } - parser.next_structural_index = 0; - if (partial == stage1_mode::streaming_partial) { - if (unclosed_string) { - parser.n_structural_indexes--; - if (simdjson_unlikely(parser.n_structural_indexes == 0)) { return CAPACITY; } - } - // We truncate the input to the end of the last complete document (or zero). - auto new_structural_indexes = find_next_document_index(parser); - if (new_structural_indexes == 0 && parser.n_structural_indexes > 0) { - if (parser.structural_indexes[0] == 0) { - // If the buffer is partial and we started at index 0 but the document is - // incomplete, it's too big to parse. - return CAPACITY; - } - else { - // It is possible that the document could be parsed, we just had a lot - // of white space. - parser.n_structural_indexes = 0; - return EMPTY; - } - } - parser.n_structural_indexes = new_structural_indexes; - } - else if (partial == stage1_mode::streaming_final) { - if (unclosed_string) { parser.n_structural_indexes--; } - // We truncate the input to the end of the last complete document (or zero). - // Because partial == stage1_mode::streaming_final, it means that we may - // silently ignore trailing garbage. Though it sounds bad, we do it - // deliberately because many people who have streams of JSON documents - // will truncate them for processing. E.g., imagine that you are uncompressing - // the data from a size file or receiving it in chunks from the network. You - // may not know where exactly the last document will be. Meanwhile the - // document_stream instances allow people to know the JSON documents they are - // parsing (see the iterator.source() method). - parser.n_structural_indexes = find_next_document_index(parser); - // We store the initial n_structural_indexes so that the client can see - // whether we used truncation. If initial_n_structural_indexes == parser.n_structural_indexes, - // then this will query parser.structural_indexes[parser.n_structural_indexes] which is len, - // otherwise, it will copy some prior index. - parser.structural_indexes[parser.n_structural_indexes + 1] = parser.structural_indexes[parser.n_structural_indexes]; - // This next line is critical, do not change it unless you understand what you are - // doing. - parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); - if (parser.n_structural_indexes == 0) { return EMPTY; } - } - else if (unclosed_string) { error = UNCLOSED_STRING; } - return error; - } +simdjson_inline bool char_is_type(uint8_t c, uint8_t type) { + return (char_table[c] & type); +} - private: - const uint8_t* buf; - uint32_t* next_structural_index; - dom_parser_implementation& parser; - uint32_t len; - uint32_t idx{ 0 }; - error_code error{ SUCCESS }; - stage1_mode partial; - }; // structural_scanner +simdjson_inline bool char_is_space(uint8_t c) { + return char_is_type(c, CHAR_TYPE_SPACE); +} - } // namespace stage1 - } // unnamed namespace +simdjson_inline bool char_is_operator(uint8_t c) { + return char_is_type(c, CHAR_TYPE_OPERATOR); +} - simdjson_warn_unused error_code dom_parser_implementation::stage1(const uint8_t* _buf, size_t _len, stage1_mode partial) noexcept { - this->buf = _buf; - this->len = _len; - stage1::structural_scanner scanner(*this, partial); - return scanner.scan(); - } +simdjson_inline bool char_is_space_or_operator(uint8_t c) { + return char_is_type(c, CHAR_TYPE_SPACE | CHAR_TYPE_OPERATOR); +} - // big table for the minifier - static uint8_t jump_table[256 * 3] = { - 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, - 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, - 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, - 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, - 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, - 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, - 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, - 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, - 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, - 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, - 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, - 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, - 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, - 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, - 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, - 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, - 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, - 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, - 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, - 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, - 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, - 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, - }; +simdjson_inline bool char_is_ascii_stop(uint8_t c) { + return char_is_type(c, CHAR_TYPE_ESC_ASCII | CHAR_TYPE_NON_ASCII); +} - simdjson_warn_unused error_code implementation::minify(const uint8_t* buf, size_t len, uint8_t* dst, size_t& dst_len) const noexcept { - size_t i = 0, pos = 0; - uint8_t quote = 0; - uint8_t nonescape = 1; +// Returns true if the string is unclosed. +simdjson_inline bool validate_string() { + idx++; // skip first quote + while (idx < len) { + do { + if (char_is_ascii_stop(buf[idx])) { break; } + idx++; + } while (idx < len); + if (idx >= len) { return true; } + if (buf[idx] == '"') { + return false; + } + if (buf[idx] == '\\') { + idx += 2; + } else if (simdjson_unlikely(buf[idx] & 0x80)) { + validate_utf8_character(); + } else { + if (buf[idx] < 0x20) { error = UNESCAPED_CHARS; } + idx++; + } + } + if (idx >= len) { return true; } + return false; +} - while (i < len) { - unsigned char c = buf[i]; - uint8_t* meta = jump_table + 3 * c; +// +// Parse the entire input in STEP_SIZE-byte chunks. +// +simdjson_warn_unused simdjson_inline error_code scan() { + bool unclosed_string = false; + for (;idx= len) { break; } + // String + if (buf[idx] == '"') { + add_structural(); + unclosed_string |= validate_string(); + // Operator + } else if (char_is_operator(buf[idx])) { + add_structural(); + // Primitive or invalid character (invalid characters will be checked in stage 2) + } else { + // Anything else, add the structural and go until we find the next one + add_structural(); + while (idx+1 0) { + if(parser.structural_indexes[0] == 0) { + // If the buffer is partial and we started at index 0 but the document is + // incomplete, it's too big to parse. + return CAPACITY; + } else { + // It is possible that the document could be parsed, we just had a lot + // of white space. + parser.n_structural_indexes = 0; + return EMPTY; + } + } + parser.n_structural_indexes = new_structural_indexes; + } else if(partial == stage1_mode::streaming_final) { + if(unclosed_string) { parser.n_structural_indexes--; } + // We truncate the input to the end of the last complete document (or zero). + // Because partial == stage1_mode::streaming_final, it means that we may + // silently ignore trailing garbage. Though it sounds bad, we do it + // deliberately because many people who have streams of JSON documents + // will truncate them for processing. E.g., imagine that you are uncompressing + // the data from a size file or receiving it in chunks from the network. You + // may not know where exactly the last document will be. Meanwhile the + // document_stream instances allow people to know the JSON documents they are + // parsing (see the iterator.source() method). + parser.n_structural_indexes = find_next_document_index(parser); + // We store the initial n_structural_indexes so that the client can see + // whether we used truncation. If initial_n_structural_indexes == parser.n_structural_indexes, + // then this will query parser.structural_indexes[parser.n_structural_indexes] which is len, + // otherwise, it will copy some prior index. + parser.structural_indexes[parser.n_structural_indexes + 1] = parser.structural_indexes[parser.n_structural_indexes]; + // This next line is critical, do not change it unless you understand what you are + // doing. + parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); + if (parser.n_structural_indexes == 0) { return EMPTY; } + } else if(unclosed_string) { error = UNCLOSED_STRING; } + return error; +} - quote = quote ^ (meta[0] & nonescape); - dst[pos] = c; - pos += meta[2] | quote; +private: + const uint8_t *buf; + uint32_t *next_structural_index; + dom_parser_implementation &parser; + uint32_t len; + uint32_t idx{0}; + error_code error{SUCCESS}; + stage1_mode partial; +}; // structural_scanner - i += 1; - nonescape = uint8_t(~nonescape) | (meta[1]); - } - dst_len = pos; // we intentionally do not work with a reference - // for fear of aliasing - return quote ? UNCLOSED_STRING : SUCCESS; - } +} // namespace stage1 +} // unnamed namespace - // credit: based on code from Google Fuchsia (Apache Licensed) - simdjson_warn_unused bool implementation::validate_utf8(const char* buf, size_t len) const noexcept { - const uint8_t* data = reinterpret_cast(buf); - uint64_t pos = 0; - uint32_t code_point = 0; - while (pos < len) { - // check of the next 8 bytes are ascii. - uint64_t next_pos = pos + 16; - if (next_pos <= len) { // if it is safe to read 8 more bytes, check that they are ascii - uint64_t v1; - memcpy(&v1, data + pos, sizeof(uint64_t)); - uint64_t v2; - memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t)); - uint64_t v{ v1 | v2 }; - if ((v & 0x8080808080808080) == 0) { - pos = next_pos; - continue; - } - } - unsigned char byte = data[pos]; - if (byte < 0x80) { - pos++; - continue; - } - else if ((byte & 0xe0) == 0xc0) { - next_pos = pos + 2; - if (next_pos > len) { return false; } - if ((data[pos + 1] & 0xc0) != 0x80) { return false; } - // range check - code_point = (byte & 0x1f) << 6 | (data[pos + 1] & 0x3f); - if (code_point < 0x80 || 0x7ff < code_point) { return false; } - } - else if ((byte & 0xf0) == 0xe0) { - next_pos = pos + 3; - if (next_pos > len) { return false; } - if ((data[pos + 1] & 0xc0) != 0x80) { return false; } - if ((data[pos + 2] & 0xc0) != 0x80) { return false; } - // range check - code_point = (byte & 0x0f) << 12 | - (data[pos + 1] & 0x3f) << 6 | - (data[pos + 2] & 0x3f); - if (code_point < 0x800 || 0xffff < code_point || - (0xd7ff < code_point && code_point < 0xe000)) { - return false; - } - } - else if ((byte & 0xf8) == 0xf0) { // 0b11110000 - next_pos = pos + 4; - if (next_pos > len) { return false; } - if ((data[pos + 1] & 0xc0) != 0x80) { return false; } - if ((data[pos + 2] & 0xc0) != 0x80) { return false; } - if ((data[pos + 3] & 0xc0) != 0x80) { return false; } - // range check - code_point = - (byte & 0x07) << 18 | (data[pos + 1] & 0x3f) << 12 | - (data[pos + 2] & 0x3f) << 6 | (data[pos + 3] & 0x3f); - if (code_point <= 0xffff || 0x10ffff < code_point) { return false; } - } - else { - // we may have a continuation - return false; - } - pos = next_pos; - } - return true; - } +simdjson_warn_unused error_code dom_parser_implementation::stage1(const uint8_t *_buf, size_t _len, stage1_mode partial) noexcept { + this->buf = _buf; + this->len = _len; + stage1::structural_scanner scanner(*this, partial); + return scanner.scan(); +} - } // namespace fallback +// big table for the minifier +static uint8_t jump_table[256 * 3] = { + 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, + 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, + 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, + 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, + 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, + 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, + 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, + 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, + 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, + 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, + 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, + 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, + 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, + 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, + 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, + 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, + 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, + 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, + 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, + 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, + 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, + 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, + 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, + 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, + 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, + 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, + 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, + 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, + 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, + 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, +}; + +simdjson_warn_unused error_code implementation::minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept { + size_t i = 0, pos = 0; + uint8_t quote = 0; + uint8_t nonescape = 1; + + while (i < len) { + unsigned char c = buf[i]; + uint8_t *meta = jump_table + 3 * c; + + quote = quote ^ (meta[0] & nonescape); + dst[pos] = c; + pos += meta[2] | quote; + + i += 1; + nonescape = uint8_t(~nonescape) | (meta[1]); + } + dst_len = pos; // we intentionally do not work with a reference + // for fear of aliasing + return quote ? UNCLOSED_STRING : SUCCESS; +} + +// credit: based on code from Google Fuchsia (Apache Licensed) +simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept { + const uint8_t *data = reinterpret_cast(buf); + uint64_t pos = 0; + uint32_t code_point = 0; + while (pos < len) { + // check of the next 8 bytes are ascii. + uint64_t next_pos = pos + 16; + if (next_pos <= len) { // if it is safe to read 8 more bytes, check that they are ascii + uint64_t v1; + memcpy(&v1, data + pos, sizeof(uint64_t)); + uint64_t v2; + memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t)); + uint64_t v{v1 | v2}; + if ((v & 0x8080808080808080) == 0) { + pos = next_pos; + continue; + } + } + unsigned char byte = data[pos]; + if (byte < 0x80) { + pos++; + continue; + } else if ((byte & 0xe0) == 0xc0) { + next_pos = pos + 2; + if (next_pos > len) { return false; } + if ((data[pos + 1] & 0xc0) != 0x80) { return false; } + // range check + code_point = (byte & 0x1f) << 6 | (data[pos + 1] & 0x3f); + if (code_point < 0x80 || 0x7ff < code_point) { return false; } + } else if ((byte & 0xf0) == 0xe0) { + next_pos = pos + 3; + if (next_pos > len) { return false; } + if ((data[pos + 1] & 0xc0) != 0x80) { return false; } + if ((data[pos + 2] & 0xc0) != 0x80) { return false; } + // range check + code_point = (byte & 0x0f) << 12 | + (data[pos + 1] & 0x3f) << 6 | + (data[pos + 2] & 0x3f); + if (code_point < 0x800 || 0xffff < code_point || + (0xd7ff < code_point && code_point < 0xe000)) { + return false; + } + } else if ((byte & 0xf8) == 0xf0) { // 0b11110000 + next_pos = pos + 4; + if (next_pos > len) { return false; } + if ((data[pos + 1] & 0xc0) != 0x80) { return false; } + if ((data[pos + 2] & 0xc0) != 0x80) { return false; } + if ((data[pos + 3] & 0xc0) != 0x80) { return false; } + // range check + code_point = + (byte & 0x07) << 18 | (data[pos + 1] & 0x3f) << 12 | + (data[pos + 2] & 0x3f) << 6 | (data[pos + 3] & 0x3f); + if (code_point <= 0xffff || 0x10ffff < code_point) { return false; } + } else { + // we may have a continuation + return false; + } + pos = next_pos; + } + return true; +} + +} // namespace fallback } // namespace simdjson // @@ -56230,31 +64954,32 @@ namespace simdjson { // namespace simdjson { - namespace fallback { +namespace fallback { - simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document& _doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); - } +simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept { + return stage2::tape_builder::parse_document(*this, _doc); +} - simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document& _doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); - } +simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept { + return stage2::tape_builder::parse_document(*this, _doc); +} - simdjson_warn_unused uint8_t* dom_parser_implementation::parse_string(const uint8_t* src, uint8_t* dst, bool replacement_char) const noexcept { - return fallback::stringparsing::parse_string(src, dst, replacement_char); - } +SIMDJSON_NO_SANITIZE_MEMORY +simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool replacement_char) const noexcept { + return fallback::stringparsing::parse_string(src, dst, replacement_char); +} - simdjson_warn_unused uint8_t* dom_parser_implementation::parse_wobbly_string(const uint8_t* src, uint8_t* dst) const noexcept { - return fallback::stringparsing::parse_wobbly_string(src, dst); - } +simdjson_warn_unused uint8_t *dom_parser_implementation::parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept { + return fallback::stringparsing::parse_wobbly_string(src, dst); +} - simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t* _buf, size_t _len, dom::document& _doc) noexcept { - auto error = stage1(_buf, _len, stage1_mode::regular); - if (error) { return error; } - return stage2(_doc); - } +simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept { + auto error = stage1(_buf, _len, stage1_mode::regular); + if (error) { return error; } + return stage2(_doc); +} - } // namespace fallback +} // namespace fallback } // namespace simdjson /* including simdjson/fallback/end.h: #include */ diff --git a/M6502/HarteTest_6502/simdjson/simdjson.h b/M6502/HarteTest_6502/simdjson/simdjson.h index ab22449..cf2658a 100644 --- a/M6502/HarteTest_6502/simdjson/simdjson.h +++ b/M6502/HarteTest_6502/simdjson/simdjson.h @@ -1,4 +1,4 @@ -/* auto-generated on 2024-08-26 09:37:03 -0400. Do not edit! */ +/* auto-generated on 2026-02-20 16:16:37 -0500. version 4.3.1 Do not edit! */ /* including simdjson.h: */ /* begin file simdjson.h */ #ifndef SIMDJSON_H @@ -37,8 +37,8 @@ } */ - /* including simdjson/common_defs.h: #include "simdjson/common_defs.h" */ - /* begin file simdjson/common_defs.h */ +/* 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 @@ -60,6 +60,11 @@ #endif #endif +// C++ 26 +#if !defined(SIMDJSON_CPLUSPLUS26) && (SIMDJSON_CPLUSPLUS >= 202402L) // update when the standard is finalized +#define SIMDJSON_CPLUSPLUS26 1 +#endif + // C++ 23 #if !defined(SIMDJSON_CPLUSPLUS23) && (SIMDJSON_CPLUSPLUS >= 202302L) #define SIMDJSON_CPLUSPLUS23 1 @@ -97,6 +102,71 @@ #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() +#include +#endif +#endif + +// The current specification is unclear on how we detect +// static reflection, both __cpp_lib_reflection and +// __cpp_impl_reflection are proposed in the draft specification. +// For now, we disable static reflect by default. It must be +// specified at compiler time. +#ifndef SIMDJSON_STATIC_REFLECTION +#define SIMDJSON_STATIC_REFLECTION 0 // disabled by default. +#endif + +#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 +#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 +#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 + + +#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) + #endif // SIMDJSON_COMPILER_CHECK_H /* end file simdjson/compiler_check.h */ /* including simdjson/portability.h: #include "simdjson/portability.h" */ @@ -109,11 +179,15 @@ #include #include #include +#include #ifndef _WIN32 // strcasecmp, strncasecmp #include #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; @@ -130,10 +204,10 @@ using std::size_t; * */ #ifdef __clang__ - // clang under visual studio +// clang under visual studio #define SIMDJSON_CLANG_VISUAL_STUDIO 1 #else - // just regular visual studio (best guess) +// just regular visual studio (best guess) #define SIMDJSON_REGULAR_VISUAL_STUDIO 1 #endif // __clang__ #endif // _MSC_VER @@ -144,9 +218,30 @@ using std::size_t; #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 +#endif #elif defined(__PPC64__) || defined(_M_PPC64) +#define SIMDJSON_IS_PPC64 1 #if defined(__ALTIVEC__) #define SIMDJSON_IS_PPC64_VMX 1 #endif // defined(__ALTIVEC__) @@ -200,7 +295,7 @@ using std::size_t; // // We are going to use runtime dispatch. -#if SIMDJSON_IS_X86_64 +#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 @@ -217,7 +312,7 @@ using std::size_t; #define SIMDJSON_UNTARGET_REGION _Pragma("GCC pop_options") #endif // clang then gcc -#endif // x86 +#endif // defined(SIMDJSON_IS_X86_64) || defined(SIMDJSON_IS_LSX) // Default target region macros don't do anything. #ifndef SIMDJSON_TARGET_REGION @@ -286,9 +381,11 @@ using std::size_t; #define simdjson_strncasecmp strncasecmp #endif -#if defined(NDEBUG) || defined(__OPTIMIZE__) || (defined(_MSC_VER) && !defined(_DEBUG)) +#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) @@ -297,35 +394,74 @@ using std::size_t; #define SIMDJSON_ASSUME(COND) do { if (!(COND)) __builtin_unreachable(); } while (0) #endif -#else // defined(NDEBUG) || defined(__OPTIMIZE__) || (defined(_MSC_VER) && !defined(_DEBUG)) +#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 +#elif defined(sun) || defined(__sun) +#include +#elif defined(__MVS__) +#include +#else +#ifdef __has_include +#if __has_include() +#include +#endif //__has_include() +#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 + + #endif // SIMDJSON_PORTABILITY_H /* end file simdjson/portability.h */ namespace simdjson { - namespace internal { - /** - * @private - * Our own implementation of the C++17 to_chars function. - * Defined in src/to_chars - */ - 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; - } +namespace internal { +/** + * @private + * Our own implementation of the C++17 to_chars function. + * Defined in src/to_chars + */ +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; +} #ifndef SIMDJSON_EXCEPTIONS -#if __cpp_exceptions +#if defined(__cpp_exceptions) || defined(_CPPUNWIND) #define SIMDJSON_EXCEPTIONS 1 #else #define SIMDJSON_EXCEPTIONS 0 @@ -336,13 +472,13 @@ 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); + #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) + #define SIMDJSON_BEGIN_DEBUG_BLOCK(name) + #define SIMDJSON_END_DEBUG_BLOCK(name) + #define SIMDJSON_DEBUG_BLOCK(name, block) #endif // Align to N-byte boundary @@ -353,67 +489,67 @@ namespace simdjson { #if SIMDJSON_REGULAR_VISUAL_STUDIO // We could use [[deprecated]] but it requires C++14 -#define simdjson_deprecated __declspec(deprecated) + #define simdjson_deprecated __declspec(deprecated) -#define simdjson_really_inline __forceinline -#define simdjson_never_inline __declspec(noinline) + #define simdjson_really_inline __forceinline + #define simdjson_never_inline __declspec(noinline) -#define simdjson_unused -#define simdjson_warn_unused + #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 + #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() -#include -#define SIMDJSON_DISABLE_UNDESIRED_WARNINGS SIMDJSON_DISABLE_VS_WARNING(ALL_CPPCORECHECK_WARNINGS) -#endif -#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() + #include + #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 + #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_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 + #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_deprecated __attribute__((deprecated)) -#define simdjson_really_inline inline __attribute__((always_inline)) -#define simdjson_never_inline inline __attribute__((noinline)) + #define simdjson_really_inline inline __attribute__((always_inline)) + #define simdjson_never_inline inline __attribute__((noinline)) -#define simdjson_unused __attribute__((unused)) -#define simdjson_warn_unused __attribute__((warn_unused_result)) + #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 + #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 \ + #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) \ @@ -425,8 +561,8 @@ namespace simdjson { 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 \ + #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) \ @@ -440,22 +576,22 @@ namespace simdjson { SIMDJSON_DISABLE_GCC_WARNING(-Wunused-variable) \ SIMDJSON_DISABLE_GCC_WARNING(-Wmaybe-uninitialized) \ SIMDJSON_DISABLE_GCC_WARNING(-Wformat-security) -#endif // __clang__ + #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_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 \ + #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 + #define SIMDJSON_POP_DISABLE_UNUSED_WARNINGS SIMDJSON_POP_DISABLE_WARNINGS @@ -467,10 +603,10 @@ namespace simdjson { // 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 + #define simdjson_inline inline #else // Force inlining for most simdjson functions. -#define simdjson_inline simdjson_really_inline + #define simdjson_inline simdjson_really_inline #endif #if SIMDJSON_VISUAL_STUDIO @@ -489,31 +625,20 @@ namespace simdjson { * * 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) + #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 - // We assume by default static linkage -#define SIMDJSON_DLLIMPORTEXPORT -#endif - -/** - * Workaround for the vcpkg package manager. Only vcpkg should - * ever touch the next line. The SIMDJSON_USING_LIBRARY macro is otherwise unused. - */ -#if SIMDJSON_USING_LIBRARY -#define SIMDJSON_DLLIMPORTEXPORT __declspec(dllimport) -#endif - /** - * End of workaround for the vcpkg package manager. - */ -#else -#define SIMDJSON_DLLIMPORTEXPORT + #define SIMDJSON_DLLIMPORTEXPORT #endif // C++17 requires string_view. @@ -528,12 +653,14 @@ namespace simdjson { // even if we do not have C++17 support. #ifdef __cpp_lib_string_view #define SIMDJSON_HAS_STRING_VIEW +#include #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) @@ -549,6 +676,7 @@ namespace simdjson { #endif // __has_include () #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. // @@ -569,7 +697,6 @@ SIMDJSON_PUSH_DISABLE_ALL_WARNINGS // 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) -#pragma once #ifndef NONSTD_SV_LITE_H_INCLUDED #define NONSTD_SV_LITE_H_INCLUDED @@ -701,56 +828,55 @@ SIMDJSON_PUSH_DISABLE_ALL_WARNINGS namespace nonstd { - template< class CharT, class Traits, class Allocator = std::allocator > - std::basic_string - to_string(std::basic_string_view v, Allocator const& a = Allocator()) - { - return std::basic_string(v.begin(), v.end(), a); - } +template< class CharT, class Traits, class Allocator = std::allocator > +std::basic_string +to_string( std::basic_string_view v, Allocator const & a = Allocator() ) +{ + return std::basic_string( v.begin(), v.end(), a ); +} - template< class CharT, class Traits, class Allocator > - std::basic_string_view - to_string_view(std::basic_string const& s) - { - return std::basic_string_view(s.data(), s.size()); - } +template< class CharT, class Traits, class Allocator > +std::basic_string_view +to_string_view( std::basic_string const & s ) +{ + return std::basic_string_view( s.data(), s.size() ); +} - // Literal operators sv and _sv: +// Literal operators sv and _sv: #if nssv_CONFIG_STD_SV_OPERATOR - using namespace std::literals::string_view_literals; +using namespace std::literals::string_view_literals; #endif #if nssv_CONFIG_USR_SV_OPERATOR - inline namespace literals { - inline namespace string_view_literals { +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::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::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::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 }; - } +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 +}} // namespace literals::string_view_literals #endif // nssv_CONFIG_USR_SV_OPERATOR @@ -760,22 +886,22 @@ namespace nonstd { namespace nonstd { - using std::string_view; - using std::wstring_view; - using std::u16string_view; - using std::u32string_view; - using std::basic_string_view; +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 +// 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==; +using std::operator!=; +using std::operator<; +using std::operator<=; +using std::operator>; +using std::operator>=; - using std::operator<<; +using std::operator<<; } // namespace nonstd @@ -973,9 +1099,9 @@ namespace nonstd { #endif #if nssv_HAVE_NODISCARD -# define nssv_nodiscard [[nodiscard]] +# define nssv_nodiscard simdjson_warn_unused #else -# define nssv_nodiscard /*[[nodiscard]]*/ +# define nssv_nodiscard /*simdjson_warn_unused*/ #endif // Additional includes: @@ -1036,721 +1162,708 @@ namespace nonstd { // 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_MSVC_WARNINGS( 4455 26481 26472 ) //nssv_DISABLE_CLANG_WARNINGS( "-Wuser-defined-literals" ) //nssv_DISABLE_GNUC_WARNINGS( -Wliteral-suffix ) -namespace nonstd { - namespace sv_lite { +namespace nonstd { namespace sv_lite { - // - // basic_string_view declaration: - // +// +// basic_string_view declaration: +// - template - < - class CharT, - class Traits = std::char_traits - > - class basic_string_view; +template +< + class CharT, + class Traits = std::char_traits +> +class basic_string_view; - namespace detail { +namespace detail { - // support constexpr comparison in C++14; - // for C++17 and later, use provided traits: +// 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; - } +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: +// 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); - } +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: +// 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); - } +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: +// 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); - } +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: +// 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; - } +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: +// gcc, clang provide __OPTIMIZE__ +// Expect tail call optimization to make search() non-recursive: - template< class CharT, class Traits = std::char_traits > - constexpr const CharT* search(basic_string_view haystack, basic_string_view needle) - { - return haystack.starts_with(needle) ? haystack.begin() : - haystack.empty() ? haystack.end() : search(haystack.substr(1), needle); - } +template< class CharT, class Traits = std::char_traits > +constexpr const CharT* search( basic_string_view haystack, basic_string_view needle ) +{ + return haystack.starts_with( needle ) ? haystack.begin() : + haystack.empty() ? haystack.end() : search( haystack.substr(1), needle ); +} #else // OPTIMIZE - // non-recursive: +// non-recursive: #if nssv_CONFIG_CONSTEXPR11_STD_SEARCH - template< class CharT, class Traits = std::char_traits > - constexpr const CharT* search(basic_string_view haystack, basic_string_view needle) - { - return std::search(haystack.begin(), haystack.end(), needle.begin(), needle.end()); - } +template< class CharT, class Traits = std::char_traits > +constexpr const CharT* search( basic_string_view haystack, basic_string_view 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 > - nssv_constexpr14 const CharT* search(basic_string_view haystack, basic_string_view needle) - { - while (needle.size() <= haystack.size()) - { - if (haystack.starts_with(needle)) - { - return haystack.cbegin(); - } - haystack = basic_string_view{ haystack.begin() + 1, haystack.size() - 1U }; - } - return haystack.cend(); - } +template< class CharT, class Traits = std::char_traits > +nssv_constexpr14 const CharT* search( basic_string_view haystack, basic_string_view needle ) +{ + while ( needle.size() <= haystack.size() ) + { + if ( haystack.starts_with(needle) ) + { + return haystack.cbegin(); + } + haystack = basic_string_view{ 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 +} // namespace detail - // - // basic_string_view: - // +// +// basic_string_view: +// - template - < - class CharT, - class Traits /* = std::char_traits */ - > - class basic_string_view - { - public: - // Member types: +template +< + class CharT, + class Traits /* = std::char_traits */ +> +class basic_string_view +{ +public: + // Member types: - typedef Traits traits_type; - typedef CharT value_type; + 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 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 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; + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; - // 24.4.2.1 Construction and assignment: + // 24.4.2.1 Construction and assignment: - nssv_constexpr basic_string_view() nssv_noexcept - : data_(nssv_nullptr) - , size_(0) - {} + 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; + 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_) - {} + 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, 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) + nssv_constexpr basic_string_view( CharT const * s) nssv_noexcept // non-standard noexcept + : data_( s ) #if nssv_CPP17_OR_GREATER - , size_(Traits::length(s)) + , size_( Traits::length(s) ) #elif nssv_CPP11_OR_GREATER - , size_(detail::length(s)) + , size_( detail::length(s) ) #else - , size_(Traits::length(s)) + , 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; + nssv_constexpr basic_string_view( std::nullptr_t ) nssv_noexcept = delete; # else - private: nssv_constexpr basic_string_view(std::nullptr_t) nssv_noexcept; public: + private: nssv_constexpr basic_string_view( std::nullptr_t ) nssv_noexcept; public: # endif #endif - // Assignment: + // Assignment: #if nssv_CPP11_OR_GREATER - nssv_constexpr14 basic_string_view& operator=(basic_string_view const& other) nssv_noexcept = default; + 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; - } + 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: + // 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 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_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 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(); } + 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: + // 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)(); } + 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_; - } + // since C++20 + nssv_nodiscard nssv_constexpr bool empty() const nssv_noexcept + { + return 0 == size_; + } - // 24.4.2.4 Element access: + // 24.4.2.4 Element access: - nssv_constexpr const_reference operator[](size_type pos) const - { - return data_at(pos); - } + nssv_constexpr const_reference operator[]( size_type pos ) const + { + return data_at( pos ); + } - nssv_constexpr14 const_reference at(size_type pos) const - { + nssv_constexpr14 const_reference at( size_type pos ) const + { #if nssv_CONFIG_NO_EXCEPTIONS - assert(pos < size()); + assert( pos < size() ); #else - if (pos >= size()) - { - throw std::out_of_range("nonstd::string_view::at()"); - } + if ( pos >= size() ) + { + throw std::out_of_range("nonstd::string_view::at()"); + } #endif - return data_at(pos); - } + 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_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_; } + nssv_constexpr const_pointer data() const nssv_noexcept { return data_; } - // 24.4.2.5 Modifiers: + // 24.4.2.5 Modifiers: - nssv_constexpr14 void remove_prefix(size_type n) - { - assert(n <= size()); - data_ += n; - size_ -= n; - } + 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 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; - } + 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: + // 24.4.2.6 String operations: - size_type copy(CharT* dest, size_type n, size_type pos = 0) const - { + size_type copy( CharT * dest, size_type n, size_type pos = 0 ) const + { #if nssv_CONFIG_NO_EXCEPTIONS - assert(pos <= size()); + assert( pos <= size() ); #else - if (pos > size()) - { - throw std::out_of_range("nonstd::string_view::copy()"); - } + if ( pos > size() ) + { + throw std::out_of_range("nonstd::string_view::copy()"); + } #endif - const size_type rlen = (std::min)(n, size() - pos); + const size_type rlen = (std::min)( n, size() - pos ); - (void)Traits::copy(dest, data() + pos, rlen); + (void) Traits::copy( dest, data() + pos, rlen ); - return rlen; - } + return rlen; + } - nssv_constexpr14 basic_string_view substr(size_type pos = 0, size_type n = npos) const - { + nssv_constexpr14 basic_string_view substr( size_type pos = 0, size_type n = npos ) const + { #if nssv_CONFIG_NO_EXCEPTIONS - assert(pos <= size()); + assert( pos <= size() ); #else - if (pos > size()) - { - throw std::out_of_range("nonstd::string_view::substr()"); - } + if ( pos > size() ) + { + throw std::out_of_range("nonstd::string_view::substr()"); + } #endif - return basic_string_view(data() + pos, (std::min)(n, size() - pos)); - } + return basic_string_view( data() + pos, (std::min)( n, size() - pos ) ); + } - // compare(), 6x: + // compare(), 6x: - nssv_constexpr14 int compare(basic_string_view other) const nssv_noexcept // (1) - { + 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()))) + 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()))) + if ( const int result = detail::compare( data(), other.data(), (std::min)( size(), other.size() ) ) ) #endif - { - return result; - } + { + return result; + } - return size() == other.size() ? 0 : size() < other.size() ? -1 : 1; - } + 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 ) 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( 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( 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 ) 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)); - } + 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: + // 24.4.2.7 Searching: - // starts_with(), 3x, since C++20: + // 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( 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 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)); - } + nssv_constexpr bool starts_with( CharT const * s ) const // (3) + { + return starts_with( basic_string_view( s ) ); + } - // ends_with(), 3x, since C++20: + // 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( 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 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)); - } + nssv_constexpr bool ends_with( CharT const * s ) const // (3) + { + return ends_with( basic_string_view( s ) ); + } - // find(), 4x: + // 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( + 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) + detail::search( substr(pos), v ) #else - std::search(cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq) + 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 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, 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); - } + nssv_constexpr size_type find( CharT const * s, size_type pos = 0 ) const // (4) + { + return find( basic_string_view( s ), pos ); + } - // rfind(), 4x: + // 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; - } + 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); - } + 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); + 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; - } + 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 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, 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); - } + 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: + // 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( 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 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, 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); - } + 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: + // 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( 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 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, 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); - } + 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: + // 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( 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 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, 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); - } + 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: + // 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( 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 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, 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); - } + 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: + // Constants: #if nssv_CPP17_OR_GREATER - static nssv_constexpr size_type npos = size_type(-1); + static nssv_constexpr size_type npos = size_type(-1); #elif nssv_CPP11_OR_GREATER - enum : size_type { npos = size_type(-1) }; + enum : size_type { npos = size_type(-1) }; #else - enum { npos = size_type(-1) }; + enum { npos = size_type(-1) }; #endif - private: - struct not_in_view - { - const basic_string_view v; +private: + struct not_in_view + { + const basic_string_view v; - nssv_constexpr explicit not_in_view(basic_string_view v_) : v(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 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_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 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 - { + nssv_constexpr const_reference data_at( size_type pos ) const + { #if nssv_BETWEEN( nssv_COMPILER_GNUC_VERSION, 1, 500 ) - return data_[pos]; + return data_[pos]; #else - return assert(pos < size()), data_[pos]; + return assert( pos < size() ), data_[pos]; #endif - } + } - private: - const_pointer data_; - size_type size_; +private: + const_pointer data_; + size_type size_; - public: +public: #if nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS - template< class Allocator > - basic_string_view(std::basic_string const& s) nssv_noexcept - : data_(s.data()) - , size_(s.size()) - {} + template< class Allocator > + basic_string_view( std::basic_string const & s ) nssv_noexcept + : data_( s.data() ) + , size_( s.size() ) + {} #if nssv_HAVE_EXPLICIT_CONVERSION - template< class Allocator > - explicit operator std::basic_string() const - { - return to_string(Allocator()); - } + template< class Allocator > + explicit operator std::basic_string() const + { + return to_string( Allocator() ); + } #endif // nssv_HAVE_EXPLICIT_CONVERSION #if nssv_CPP11_OR_GREATER - template< class Allocator = std::allocator > - std::basic_string - to_string(Allocator const& a = Allocator()) const - { - return std::basic_string(begin(), end(), a); - } + template< class Allocator = std::allocator > + std::basic_string + to_string( Allocator const & a = Allocator() ) const + { + return std::basic_string( begin(), end(), a ); + } #else - std::basic_string - to_string() const - { - return std::basic_string(begin(), end()); - } + std::basic_string + to_string() const + { + return std::basic_string( begin(), end() ); + } - template< class Allocator > - std::basic_string - to_string(Allocator const& a) const - { - return std::basic_string(begin(), end(), a); - } + template< class Allocator > + std::basic_string + to_string( Allocator const & a ) const + { + return std::basic_string( begin(), end(), a ); + } #endif // nssv_CPP11_OR_GREATER #endif // nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS - }; +}; - // - // Non-member functions: - // +// +// Non-member functions: +// - // 24.4.3 Non-member comparison functions: - // lexicographically compare two string views (function template): +// 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 lhs, - basic_string_view rhs) nssv_noexcept - { - return lhs.size() == rhs.size() && lhs.compare(rhs) == 0; - } +template< class CharT, class Traits > +nssv_constexpr bool operator== ( + basic_string_view lhs, + basic_string_view rhs ) nssv_noexcept +{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; } - template< class CharT, class Traits > - nssv_constexpr bool operator!= ( - basic_string_view lhs, - basic_string_view rhs) nssv_noexcept - { - return !(lhs == rhs); - } +template< class CharT, class Traits > +nssv_constexpr bool operator!= ( + basic_string_view lhs, + basic_string_view rhs ) nssv_noexcept +{ return !( lhs == rhs ); } - template< class CharT, class Traits > - nssv_constexpr bool operator< ( - basic_string_view lhs, - basic_string_view rhs) nssv_noexcept - { - return lhs.compare(rhs) < 0; - } +template< class CharT, class Traits > +nssv_constexpr bool operator< ( + basic_string_view lhs, + basic_string_view rhs ) nssv_noexcept +{ return lhs.compare( rhs ) < 0; } - template< class CharT, class Traits > - nssv_constexpr bool operator<= ( - basic_string_view lhs, - basic_string_view rhs) nssv_noexcept - { - return lhs.compare(rhs) <= 0; - } +template< class CharT, class Traits > +nssv_constexpr bool operator<= ( + basic_string_view lhs, + basic_string_view rhs ) nssv_noexcept +{ return lhs.compare( rhs ) <= 0; } - template< class CharT, class Traits > - nssv_constexpr bool operator> ( - basic_string_view lhs, - basic_string_view rhs) nssv_noexcept - { - return lhs.compare(rhs) > 0; - } +template< class CharT, class Traits > +nssv_constexpr bool operator> ( + basic_string_view lhs, + basic_string_view rhs ) nssv_noexcept +{ return lhs.compare( rhs ) > 0; } - template< class CharT, class Traits > - nssv_constexpr bool operator>= ( - basic_string_view lhs, - basic_string_view rhs) nssv_noexcept - { - return lhs.compare(rhs) >= 0; - } +template< class CharT, class Traits > +nssv_constexpr bool operator>= ( + basic_string_view lhs, + basic_string_view rhs ) nssv_noexcept +{ return lhs.compare( rhs ) >= 0; } - // Let S be basic_string_view, 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. +// Let S be basic_string_view, 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 ) @@ -1758,207 +1871,159 @@ namespace nonstd { // == - template< class CharT, class Traits> - nssv_constexpr bool operator==( - basic_string_view 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==( + basic_string_view 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 rhs) nssv_noexcept - { - return detail::length(lhs) == rhs.size() && rhs.compare(lhs) == 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator==( + CharT const * lhs, + basic_string_view 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 lhs, - std::basic_string rhs) nssv_noexcept - { - return lhs.size() == rhs.size() && lhs.compare(rhs) == 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator==( + basic_string_view lhs, + std::basic_string rhs ) nssv_noexcept +{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; } - template< class CharT, class Traits> - nssv_constexpr bool operator==( - std::basic_string rhs, - basic_string_view lhs) nssv_noexcept - { - return lhs.size() == rhs.size() && lhs.compare(rhs) == 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator==( + std::basic_string rhs, + basic_string_view lhs ) nssv_noexcept +{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; } - // != +// != - template< class CharT, class Traits> - nssv_constexpr bool operator!=( - basic_string_view lhs, - CharT const* rhs) nssv_noexcept - { - return !(lhs == rhs); - } +template< class CharT, class Traits> +nssv_constexpr bool operator!=( + basic_string_view lhs, + CharT const * rhs ) nssv_noexcept +{ return !( lhs == rhs ); } - template< class CharT, class Traits> - nssv_constexpr bool operator!=( - CharT const* lhs, - basic_string_view rhs) nssv_noexcept - { - return !(lhs == rhs); - } +template< class CharT, class Traits> +nssv_constexpr bool operator!=( + CharT const * lhs, + basic_string_view rhs ) nssv_noexcept +{ return !( lhs == rhs ); } - template< class CharT, class Traits> - nssv_constexpr bool operator!=( - basic_string_view lhs, - std::basic_string rhs) nssv_noexcept - { - return !(lhs == rhs); - } +template< class CharT, class Traits> +nssv_constexpr bool operator!=( + basic_string_view lhs, + std::basic_string rhs ) nssv_noexcept +{ return !( lhs == rhs ); } - template< class CharT, class Traits> - nssv_constexpr bool operator!=( - std::basic_string rhs, - basic_string_view lhs) nssv_noexcept - { - return !(lhs == rhs); - } +template< class CharT, class Traits> +nssv_constexpr bool operator!=( + std::basic_string rhs, + basic_string_view lhs ) nssv_noexcept +{ return !( lhs == rhs ); } - // < +// < - template< class CharT, class Traits> - nssv_constexpr bool operator<( - basic_string_view lhs, - CharT const* rhs) nssv_noexcept - { - return lhs.compare(rhs) < 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator<( + basic_string_view 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 rhs) nssv_noexcept - { - return rhs.compare(lhs) > 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator<( + CharT const * lhs, + basic_string_view rhs ) nssv_noexcept +{ return rhs.compare( lhs ) > 0; } - template< class CharT, class Traits> - nssv_constexpr bool operator<( - basic_string_view lhs, - std::basic_string rhs) nssv_noexcept - { - return lhs.compare(rhs) < 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator<( + basic_string_view lhs, + std::basic_string rhs ) nssv_noexcept +{ return lhs.compare( rhs ) < 0; } - template< class CharT, class Traits> - nssv_constexpr bool operator<( - std::basic_string rhs, - basic_string_view lhs) nssv_noexcept - { - return rhs.compare(lhs) > 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator<( + std::basic_string rhs, + basic_string_view lhs ) nssv_noexcept +{ return rhs.compare( lhs ) > 0; } - // <= +// <= - template< class CharT, class Traits> - nssv_constexpr bool operator<=( - basic_string_view lhs, - CharT const* rhs) nssv_noexcept - { - return lhs.compare(rhs) <= 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator<=( + basic_string_view 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 rhs) nssv_noexcept - { - return rhs.compare(lhs) >= 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator<=( + CharT const * lhs, + basic_string_view rhs ) nssv_noexcept +{ return rhs.compare( lhs ) >= 0; } - template< class CharT, class Traits> - nssv_constexpr bool operator<=( - basic_string_view lhs, - std::basic_string rhs) nssv_noexcept - { - return lhs.compare(rhs) <= 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator<=( + basic_string_view lhs, + std::basic_string rhs ) nssv_noexcept +{ return lhs.compare( rhs ) <= 0; } - template< class CharT, class Traits> - nssv_constexpr bool operator<=( - std::basic_string rhs, - basic_string_view lhs) nssv_noexcept - { - return rhs.compare(lhs) >= 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator<=( + std::basic_string rhs, + basic_string_view lhs ) nssv_noexcept +{ return rhs.compare( lhs ) >= 0; } - // > +// > - template< class CharT, class Traits> - nssv_constexpr bool operator>( - basic_string_view lhs, - CharT const* rhs) nssv_noexcept - { - return lhs.compare(rhs) > 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator>( + basic_string_view 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 rhs) nssv_noexcept - { - return rhs.compare(lhs) < 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator>( + CharT const * lhs, + basic_string_view rhs ) nssv_noexcept +{ return rhs.compare( lhs ) < 0; } - template< class CharT, class Traits> - nssv_constexpr bool operator>( - basic_string_view lhs, - std::basic_string rhs) nssv_noexcept - { - return lhs.compare(rhs) > 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator>( + basic_string_view lhs, + std::basic_string rhs ) nssv_noexcept +{ return lhs.compare( rhs ) > 0; } - template< class CharT, class Traits> - nssv_constexpr bool operator>( - std::basic_string rhs, - basic_string_view lhs) nssv_noexcept - { - return rhs.compare(lhs) < 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator>( + std::basic_string rhs, + basic_string_view lhs ) nssv_noexcept +{ return rhs.compare( lhs ) < 0; } - // >= +// >= - template< class CharT, class Traits> - nssv_constexpr bool operator>=( - basic_string_view lhs, - CharT const* rhs) nssv_noexcept - { - return lhs.compare(rhs) >= 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator>=( + basic_string_view 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 rhs) nssv_noexcept - { - return rhs.compare(lhs) <= 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator>=( + CharT const * lhs, + basic_string_view rhs ) nssv_noexcept +{ return rhs.compare( lhs ) <= 0; } - template< class CharT, class Traits> - nssv_constexpr bool operator>=( - basic_string_view lhs, - std::basic_string rhs) nssv_noexcept - { - return lhs.compare(rhs) >= 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator>=( + basic_string_view lhs, + std::basic_string rhs ) nssv_noexcept +{ return lhs.compare( rhs ) >= 0; } - template< class CharT, class Traits> - nssv_constexpr bool operator>=( - std::basic_string rhs, - basic_string_view lhs) nssv_noexcept - { - return rhs.compare(lhs) <= 0; - } +template< class CharT, class Traits> +nssv_constexpr bool operator>=( + std::basic_string rhs, + basic_string_view lhs ) nssv_noexcept +{ return rhs.compare( lhs ) <= 0; } #else // newer compilers: @@ -1972,183 +2037,158 @@ namespace nonstd { // == - template< class CharT, class Traits nssv_MSVC_ORDER(1) > - nssv_constexpr bool operator==( - basic_string_view 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(1) > +nssv_constexpr bool operator==( + basic_string_view 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 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 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(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(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(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(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(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(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(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(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(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(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: +// 24.4.4 Inserters and extractors: #if ! nssv_CONFIG_NO_STREAM_INSERTION - namespace detail { +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 > +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); +template< class Stream, class View > +Stream & write_to_stream( Stream & os, View const & sv ) +{ + typename Stream::sentry sentry( os ); - if (!sentry) - return os; + if ( !sentry ) + return os; - const std::streamsize length = static_cast(sv.length()); + const std::streamsize length = static_cast( 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; + // 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); + if ( left_pad ) + write_padding( os, os.width() - length ); - // Write span characters: - os.rdbuf()->sputn(sv.begin(), length); + // Write span characters: + os.rdbuf()->sputn( sv.begin(), length ); - if (pad && !left_pad) - write_padding(os, os.width() - length); + if ( pad && !left_pad ) + write_padding( os, os.width() - length ); - // Reset output stream width: - os.width(0); + // Reset output stream width: + os.width( 0 ); - return os; - } + return os; +} - } // namespace detail +} // namespace detail - template< class CharT, class Traits > - std::basic_ostream& - operator<<( - std::basic_ostream& os, - basic_string_view sv) - { - return detail::write_to_stream(os, sv); - } +template< class CharT, class Traits > +std::basic_ostream & +operator<<( + std::basic_ostream& os, + basic_string_view sv ) +{ + return detail::write_to_stream( os, sv ); +} #endif // nssv_CONFIG_NO_STREAM_INSERTION - // Several typedefs for common character types are provided: +// Several typedefs for common character types are provided: - typedef basic_string_view string_view; - typedef basic_string_view wstring_view; +typedef basic_string_view string_view; +typedef basic_string_view wstring_view; #if nssv_HAVE_WCHAR16_T - typedef basic_string_view u16string_view; - typedef basic_string_view u32string_view; +typedef basic_string_view u16string_view; +typedef basic_string_view u32string_view; #endif - } -} // namespace nonstd::sv_lite +}} // namespace nonstd::sv_lite // // 24.4.6 Suffix for basic_string_view literals: @@ -2157,60 +2197,58 @@ namespace nonstd { #if nssv_HAVE_USER_DEFINED_LITERALS namespace nonstd { - nssv_inline_ns namespace literals { - nssv_inline_ns namespace string_view_literals { +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::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::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::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 }; - } +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::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::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::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 }; - } +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 +}}} // namespace nonstd::literals::string_view_literals #endif @@ -2221,46 +2259,45 @@ namespace nonstd { #if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS namespace nonstd { - namespace sv_lite { +namespace sv_lite { - // Exclude MSVC 14 (19.00): it yields ambiguous to_string(): +// 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 > - std::basic_string - to_string(basic_string_view v, Allocator const& a = Allocator()) - { - return std::basic_string(v.begin(), v.end(), a); - } +template< class CharT, class Traits, class Allocator = std::allocator > +std::basic_string +to_string( basic_string_view v, Allocator const & a = Allocator() ) +{ + return std::basic_string( v.begin(), v.end(), a ); +} #else - template< class CharT, class Traits > - std::basic_string - to_string(basic_string_view v) - { - return std::basic_string(v.begin(), v.end()); - } +template< class CharT, class Traits > +std::basic_string +to_string( basic_string_view v ) +{ + return std::basic_string( v.begin(), v.end() ); +} - template< class CharT, class Traits, class Allocator > - std::basic_string - to_string(basic_string_view v, Allocator const& a) - { - return std::basic_string(v.begin(), v.end(), a); - } +template< class CharT, class Traits, class Allocator > +std::basic_string +to_string( basic_string_view v, Allocator const & a ) +{ + return std::basic_string( v.begin(), v.end(), a ); +} #endif // nssv_CPP11_OR_GREATER - template< class CharT, class Traits, class Allocator > - basic_string_view - to_string_view(std::basic_string const& s) - { - return basic_string_view(s.data(), s.size()); - } +template< class CharT, class Traits, class Allocator > +basic_string_view +to_string_view( std::basic_string const & s ) +{ + return basic_string_view( s.data(), s.size() ); +} - } -} // namespace nonstd::sv_lite +}} // namespace nonstd::sv_lite #endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS @@ -2270,33 +2307,33 @@ namespace nonstd { namespace nonstd { - using sv_lite::basic_string_view; - using sv_lite::string_view; - using sv_lite::wstring_view; +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; +using sv_lite::u16string_view; #endif #if nssv_HAVE_WCHAR32_T - using sv_lite::u32string_view; +using sv_lite::u32string_view; #endif - // literal "sv" +// 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>=; +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<<; +using sv_lite::operator<<; #endif #if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS - using sv_lite::to_string; - using sv_lite::to_string_view; +using sv_lite::to_string; +using sv_lite::to_string_view; #endif } // namespace nonstd @@ -2312,45 +2349,45 @@ namespace nonstd { namespace std { - template<> - struct hash< nonstd::string_view > +template<> +struct hash< nonstd::string_view > +{ +public: + std::size_t operator()( nonstd::string_view v ) const nssv_noexcept { - public: - std::size_t operator()(nonstd::string_view v) const nssv_noexcept - { - return std::hash()(std::string(v.data(), v.size())); - } - }; + return std::hash()( std::string( v.data(), v.size() ) ); + } +}; - template<> - struct hash< nonstd::wstring_view > +template<> +struct hash< nonstd::wstring_view > +{ +public: + std::size_t operator()( nonstd::wstring_view v ) const nssv_noexcept { - public: - std::size_t operator()(nonstd::wstring_view v) const nssv_noexcept - { - return std::hash()(std::wstring(v.data(), v.size())); - } - }; + return std::hash()( std::wstring( v.data(), v.size() ) ); + } +}; - template<> - struct hash< nonstd::u16string_view > +template<> +struct hash< nonstd::u16string_view > +{ +public: + std::size_t operator()( nonstd::u16string_view v ) const nssv_noexcept { - public: - std::size_t operator()(nonstd::u16string_view v) const nssv_noexcept - { - return std::hash()(std::u16string(v.data(), v.size())); - } - }; + return std::hash()( std::u16string( v.data(), v.size() ) ); + } +}; - template<> - struct hash< nonstd::u32string_view > +template<> +struct hash< nonstd::u32string_view > +{ +public: + std::size_t operator()( nonstd::u32string_view v ) const nssv_noexcept { - public: - std::size_t operator()(nonstd::u32string_view v) const nssv_noexcept - { - return std::hash()(std::u32string(v.data(), v.size())); - } - }; + return std::hash()( std::u32string( v.data(), v.size() ) ); + } +}; } // namespace std @@ -2364,7 +2401,7 @@ nssv_RESTORE_WARNINGS() SIMDJSON_POP_DISABLE_WARNINGS namespace std { - using string_view = nonstd::string_view; + 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. @@ -2382,16 +2419,27 @@ namespace std { // 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. -#ifdef _DEBUG +// 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. -#ifndef __OPTIMIZE__ +// 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 @@ -2447,6 +2495,18 @@ namespace std { #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 */ @@ -2460,23 +2520,23 @@ namespace std { #define SIMDJSON_SIMDJSON_VERSION_H /** The version of simdjson being used (major.minor.revision) */ -#define SIMDJSON_VERSION "3.10.1" +#define SIMDJSON_VERSION "4.3.1" namespace simdjson { - enum { - /** - * The major version (MAJOR.minor.revision) of simdjson being used. - */ - SIMDJSON_VERSION_MAJOR = 3, - /** - * The minor version (major.MINOR.revision) of simdjson being used. - */ - SIMDJSON_VERSION_MINOR = 10, - /** - * The revision (major.minor.REVISION) of simdjson being used. - */ - SIMDJSON_VERSION_REVISION = 1 - }; +enum { + /** + * The major version (MAJOR.minor.revision) of simdjson being used. + */ + SIMDJSON_VERSION_MAJOR = 4, + /** + * The minor version (major.MINOR.revision) of simdjson being used. + */ + SIMDJSON_VERSION_MINOR = 3, + /** + * The revision (major.minor.REVISION) of simdjson being used. + */ + SIMDJSON_VERSION_REVISION = 1 +}; } // namespace simdjson #endif // SIMDJSON_SIMDJSON_VERSION_H @@ -2491,10 +2551,10 @@ namespace simdjson { #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 */ +/* 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 @@ -2505,362 +2565,730 @@ namespace simdjson { 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 - 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. - 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 - NUM_ERROR_CODES - }; +/** + * 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. + NUM_ERROR_CODES ///< Placeholder for end of error code list. +}; - /** - * 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. - */ +/** + * 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; - /** - * 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; +/** + * 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. + */ - /** - * Write the error message to the output stream - */ - inline std::ostream& operator<<(std::ostream& out, error_code error) noexcept; +/** + * 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; - /** - * 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 { return error_message(error()); } - /** The error code */ - error_code error() const noexcept { return _error; } - private: - /** The error code that was used */ - error_code _error; - }; +/** + * Write the error message to the output stream + */ +inline std::ostream& operator<<(std::ostream& out, error_code error) noexcept; - namespace internal { +/** + * 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; +}; - /** - * 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 : public internal::simdjson_result_base { - * simdjson_result() noexcept : internal::simdjson_result_base() {} - * simdjson_result(error_code error) noexcept : internal::simdjson_result_base(error) {} - * simdjson_result(T &&value) noexcept : internal::simdjson_result_base(std::forward(value)) {} - * simdjson_result(T &&value, error_code error) noexcept : internal::simdjson_result_base(value, error) {} - * // Your extra methods here - * } - * - * Then any method returning simdjson_result will be chainable with your methods. - */ - template - struct simdjson_result_base : protected std::pair { +namespace internal { - /** - * Create a new empty result with error = UNINITIALIZED. - */ - simdjson_inline simdjson_result_base() noexcept; +/** + * The result of a simdjson operation that could fail. + * + * IMPORTANT: For the ondemand API, we use implementation_simdjson_result_base as a base class + * to avoid some compilation issue. Thus, if you modify this class, please ensure that the ondemand + * implementation_simdjson_result_base 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 : public internal::simdjson_result_base { + * simdjson_result() noexcept : internal::simdjson_result_base() {} + * simdjson_result(error_code error) noexcept : internal::simdjson_result_base(error) {} + * simdjson_result(T &&value) noexcept : internal::simdjson_result_base(std::forward(value)) {} + * simdjson_result(T &&value, error_code error) noexcept : internal::simdjson_result_base(value, error) {} + * // Your extra methods here + * } + * + * Then any method returning simdjson_result will be chainable with your methods. + */ +template +struct simdjson_result_base : protected std::pair { - /** - * Create a new error result. - */ - simdjson_inline simdjson_result_base(error_code error) noexcept; + /** + * Create a new empty result with error = UNINITIALIZED. + */ + simdjson_inline simdjson_result_base() noexcept; - /** - * Create a new successful result. - */ - simdjson_inline simdjson_result_base(T&& value) noexcept; + /** + * Create a new error result. + */ + simdjson_inline simdjson_result_base(error_code error) 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; + /** + * Create a new successful result. + */ + simdjson_inline simdjson_result_base(T &&value) 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; + /** + * 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 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; + /** + * 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; - /** - * The error. - */ - simdjson_inline error_code error() const 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 - /** - * Get the result value. - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline T& value() & noexcept(false); + /** + * 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); - /** - * Take the result value (move it). - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline T&& value() && 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&& take_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); - /** - * 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. + * 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. - */ - 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. + * 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; - }; // struct simdjson_result_base + using value_type = T; + using error_type = error_code; +}; // struct simdjson_result_base - } // namespace internal +} // 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 - struct simdjson_result : public internal::simdjson_result_base { - /** - * @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; +/** + * 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 +struct simdjson_result : public internal::simdjson_result_base { - /** - * 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; + /** + * @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 + simdjson_warn_unused simdjson_inline error_code get(std::string &value) && noexcept { + static_assert(std::is_same::value, "SFINAE"); + std::string_view v; + error_code error = std::forward>(*this).get(v); + if (!error) { + value.assign(v.data(), v.size()); + } + return error; + } + + /** + * The error. + */ + simdjson_inline error_code error() const 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_inline error_code error() const noexcept; #if SIMDJSON_EXCEPTIONS + using internal::simdjson_result_base::operator*; + using internal::simdjson_result_base::operator->; + /** + * Get the result value. + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T& value() & 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&& value() && noexcept(false); + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& take_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); + /** + * 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 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; + /** + * 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; - }; // struct simdjson_result + using value_type = T; + using error_type = error_code; +}; // struct simdjson_result #if SIMDJSON_EXCEPTIONS - template - inline std::ostream& operator<<(std::ostream& out, simdjson_result value) { return out << value.value(); } +template +inline std::ostream& operator<<(std::ostream& out, simdjson_result 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 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; +/** + * @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 +#include + +namespace simdjson { +namespace concepts { + +namespace details { +#define SIMDJSON_IMPL_CONCEPT(name, method) \ + template \ + concept supports_##name = !std::is_const_v && requires { \ + typename std::remove_cvref_t::value_type; \ + requires requires(typename std::remove_cvref_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 +concept is_pair = requires { typename T::first_type; typename T::second_type; } && + std::same_as>; +template +concept string_view_like = std::is_convertible_v && + !std::is_convertible_v; + +template +concept constructible_from_string_view = std::is_constructible_v + && !std::is_same_v + && std::is_default_constructible_v; + +template +concept string_view_keyed_map = string_view_like + && requires(std::remove_cvref_t& m, typename M::key_type sv, typename M::mapped_type v) { + { m.emplace(sv, v) } -> std::same_as>; +}; + +/// Check if T is a container that we can append to, including: +/// std::vector, std::deque, std::list, std::string, ... +template +concept appendable_containers = + (details::supports_emplace_back || details::supports_emplace || + details::supports_push_back || details::supports_push || + details::supports_add || details::supports_append || + details::supports_insert) && !string_view_keyed_map; + +/// Insert into the container however possible +template +constexpr decltype(auto) emplace_one(T &vec, Args &&...args) { + if constexpr (details::supports_emplace_back) { + return vec.emplace_back(std::forward(args)...); + } else if constexpr (details::supports_emplace) { + return vec.emplace(std::forward(args)...); + } else if constexpr (details::supports_push_back) { + return vec.push_back(std::forward(args)...); + } else if constexpr (details::supports_push) { + return vec.push(std::forward(args)...); + } else if constexpr (details::supports_add) { + return vec.add(std::forward(args)...); + } else if constexpr (details::supports_append) { + return vec.append(std::forward(args)...); + } else if constexpr (details::supports_insert) { + return vec.insert(std::forward(args)...); + } else if constexpr (details::supports_op_append && sizeof...(Args) == 1) { + return vec.operator+=(std::forward(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 +concept returns_reference = appendable_containers && requires { + typename std::remove_cvref_t::reference; + requires requires(typename std::remove_cvref_t::value_type &&val, T obj) { + { + emplace_one(obj, std::move(val)) + } -> std::same_as::reference>; + }; +}; + +template +concept smart_pointer = requires(std::remove_cvref_t ptr) { + // Check if T has a member type named element_type + typename std::remove_cvref_t::element_type; + + // Check if T has a get() member function + { + ptr.get() + } -> std::same_as::element_type *>; + + // Check if T can be dereferenced + { *ptr } -> std::same_as::element_type &>; +}; + +template +concept optional_type = requires(std::remove_cvref_t obj) { + typename std::remove_cvref_t::value_type; + { obj.value() } -> std::same_as::value_type&>; + requires requires(typename std::remove_cvref_t::value_type &&val) { + obj.emplace(std::move(val)); + { + obj.value_or(val) + } -> std::convertible_to::value_type>; + }; + { static_cast(obj) } -> std::same_as; // convertible to bool + { obj.reset() } noexcept -> std::same_as; +}; + + + +// Types we serialize as JSON strings (not as containers) +template +concept string_like = + std::is_same_v, std::string> || + std::is_same_v, std::string_view> || + std::is_same_v, const char*> || + std::is_same_v, 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 +template +concept container_but_not_string = + std::ranges::input_range && !string_like && !concepts::string_view_keyed_map; + + + +// 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 +concept indexable_container = requires { + typename Container::value_type; + requires !concepts::string_like; + requires !requires { typename Container::key_type; }; // Reject maps/sets + requires requires(Container& c, std::size_t i) { + { c[i] } -> std::convertible_to; + }; +}; + + +// Variable template to use with std::meta::substitute +template +constexpr bool indexable_container_v = indexable_container; + +} // namespace concepts + + +/** + * We use tag_invoke as our customization point mechanism. + */ +template +concept tag_invocable = requires(Tag tag, Args... args) { + tag_invoke(std::forward(tag), std::forward(args)...); +}; + +template +concept nothrow_tag_invocable = + tag_invocable && requires(Tag tag, Args... args) { + { + tag_invoke(std::forward(tag), std::forward(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 +#include +#include + +namespace simdjson { +namespace constevalutil { +#if SIMDJSON_CONSTEVAL + +constexpr static std::array 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 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; +} +#endif // SIMDJSON_CONSTEVAL + + +#if SIMDJSON_SUPPORTS_CONCEPTS +template +struct fixed_string { + constexpr fixed_string(const char (&str)[N]) { + for (std::size_t i = 0; i < N; ++i) { + data[i] = 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 +fixed_string(const char (&)[N]) -> fixed_string; + +template +struct string_constant { + static constexpr std::string_view value = str.view(); +}; +#endif // SIMDJSON_SUPPORTS_CONCEPTS + +} // namespace constevalutil +} // namespace simdjson +#endif // SIMDJSON_CONSTEVALUTIL_H +/* end file simdjson/constevalutil.h */ /** * @brief The top level simdjson namespace, containing everything the library provides. */ namespace simdjson { - SIMDJSON_PUSH_DISABLE_UNUSED_WARNINGS +SIMDJSON_PUSH_DISABLE_UNUSED_WARNINGS - /** The maximum document size supported by simdjson. */ - constexpr size_t SIMDJSON_MAXSIZE_BYTES = 0xFFFFFFFF; +/** The maximum document size supported by simdjson. */ +constexpr size_t SIMDJSON_MAXSIZE_BYTES = 0xFFFFFFFF; - /** - * 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; +/** + * 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; +/** + * 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 +SIMDJSON_POP_DISABLE_UNUSED_WARNINGS - class implementation; - struct padded_string; - class padded_string_view; - enum class stage1_mode; +class implementation; +struct padded_string; +class padded_string_view; +enum class stage1_mode; - namespace internal { +namespace internal { - template - class atomic_ptr; - class dom_parser_implementation; - class escape_json_string; - class tape_ref; - struct value128; - enum class tape_type; +template +class atomic_ptr; +class dom_parser_implementation; +class escape_json_string; +class tape_ref; +struct value128; +enum class tape_type; - } // namespace internal +} // namespace internal } // namespace simdjson #endif // SIMDJSON_BASE_H @@ -2877,178 +3305,215 @@ namespace simdjson { #include namespace simdjson { - 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 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; - } +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 +// 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; - } +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); - } +inline std::ostream& operator<<(std::ostream& out, error_code error) noexcept { + return out << error_message(error); +} - namespace internal { +namespace internal { - // - // internal::simdjson_result_base inline implementation - // +// +// internal::simdjson_result_base inline implementation +// - template - simdjson_inline void simdjson_result_base::tie(T& value, error_code& error) && noexcept { - error = this->second; - if (!error) { - value = std::forward>(*this).first; - } - } +template +simdjson_inline void simdjson_result_base::tie(T &value, error_code &error) && noexcept { + error = this->second; + if (!error) { + value = std::forward>(*this).first; + } +} - template - simdjson_warn_unused simdjson_inline error_code simdjson_result_base::get(T& value) && noexcept { - error_code error; - std::forward>(*this).tie(value, error); - return error; - } +template +simdjson_warn_unused simdjson_inline error_code simdjson_result_base::get(T &value) && noexcept { + error_code error; + std::forward>(*this).tie(value, error); + return error; +} - template - simdjson_inline error_code simdjson_result_base::error() const noexcept { - return this->second; - } +template +simdjson_inline error_code simdjson_result_base::error() const noexcept { + return this->second; +} + + +template +simdjson_inline bool simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} #if SIMDJSON_EXCEPTIONS - template - simdjson_inline T& simdjson_result_base::value() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return this->first; - } - template - simdjson_inline T&& simdjson_result_base::value() && noexcept(false) { - return std::forward>(*this).take_value(); - } +template +simdjson_inline T& simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} - template - simdjson_inline T&& simdjson_result_base::take_value() && noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return std::forward(this->first); - } +template +simdjson_inline T&& simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} - template - simdjson_inline simdjson_result_base::operator T && () && noexcept(false) { - return std::forward>(*this).take_value(); - } +template +simdjson_inline T* simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + + +template +simdjson_inline const T* simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + +template +simdjson_inline T& simdjson_result_base::value() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return this->first; +} + +template +simdjson_inline T&& simdjson_result_base::value() && noexcept(false) { + return std::forward>(*this).take_value(); +} + +template +simdjson_inline T&& simdjson_result_base::take_value() && noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return std::forward(this->first); +} + +template +simdjson_inline simdjson_result_base::operator T&&() && noexcept(false) { + return std::forward>(*this).take_value(); +} #endif // SIMDJSON_EXCEPTIONS - template - simdjson_inline const T& simdjson_result_base::value_unsafe() const& noexcept { - return this->first; - } - template - simdjson_inline T&& simdjson_result_base::value_unsafe() && noexcept { - return std::forward(this->first); - } +template +simdjson_inline const T& simdjson_result_base::value_unsafe() const& noexcept { + return this->first; +} - template - simdjson_inline simdjson_result_base::simdjson_result_base(T&& value, error_code error) noexcept - : std::pair(std::forward(value), error) {} - template - simdjson_inline simdjson_result_base::simdjson_result_base(error_code error) noexcept - : simdjson_result_base(T{}, error) {} - template - simdjson_inline simdjson_result_base::simdjson_result_base(T&& value) noexcept - : simdjson_result_base(std::forward(value), SUCCESS) {} - template - simdjson_inline simdjson_result_base::simdjson_result_base() noexcept - : simdjson_result_base(T{}, UNINITIALIZED) {} +template +simdjson_inline T&& simdjson_result_base::value_unsafe() && noexcept { + return std::forward(this->first); +} - } // namespace internal +template +simdjson_inline simdjson_result_base::simdjson_result_base(T &&value, error_code error) noexcept + : std::pair(std::forward(value), error) {} +template +simdjson_inline simdjson_result_base::simdjson_result_base(error_code error) noexcept + : simdjson_result_base(T{}, error) {} +template +simdjson_inline simdjson_result_base::simdjson_result_base(T &&value) noexcept + : simdjson_result_base(std::forward(value), SUCCESS) {} +template +simdjson_inline simdjson_result_base::simdjson_result_base() noexcept + : simdjson_result_base(T{}, UNINITIALIZED) {} - /// - /// simdjson_result inline implementation - /// +} // namespace internal - template - simdjson_inline void simdjson_result::tie(T& value, error_code& error) && noexcept { - std::forward>(*this).tie(value, error); - } +/// +/// simdjson_result inline implementation +/// - template - simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T& value) && noexcept { - return std::forward>(*this).get(value); - } +template +simdjson_inline void simdjson_result::tie(T &value, error_code &error) && noexcept { + std::forward>(*this).tie(value, error); +} - template - simdjson_inline error_code simdjson_result::error() const noexcept { - return internal::simdjson_result_base::error(); - } +template +simdjson_warn_unused simdjson_inline error_code +simdjson_result::get(T &value) && noexcept { + return std::forward>(*this).get(value); +} + +template +simdjson_inline error_code simdjson_result::error() const noexcept { + return internal::simdjson_result_base::error(); +} #if SIMDJSON_EXCEPTIONS - template - simdjson_inline T& simdjson_result::value() & noexcept(false) { - return internal::simdjson_result_base::value(); - } +template +simdjson_inline T& simdjson_result::value() & noexcept(false) { + return internal::simdjson_result_base::value(); +} - template - simdjson_inline T&& simdjson_result::value() && noexcept(false) { - return std::forward>(*this).value(); - } +template +simdjson_inline T&& simdjson_result::value() && noexcept(false) { + return std::forward>(*this).value(); +} - template - simdjson_inline T&& simdjson_result::take_value() && noexcept(false) { - return std::forward>(*this).take_value(); - } +template +simdjson_inline T&& simdjson_result::take_value() && noexcept(false) { + return std::forward>(*this).take_value(); +} - template - simdjson_inline simdjson_result::operator T && () && noexcept(false) { - return std::forward>(*this).take_value(); - } +template +simdjson_inline simdjson_result::operator T&&() && noexcept(false) { + return std::forward>(*this).take_value(); +} #endif // SIMDJSON_EXCEPTIONS - template - simdjson_inline const T& simdjson_result::value_unsafe() const& noexcept { - return internal::simdjson_result_base::value_unsafe(); - } +template +simdjson_inline const T& simdjson_result::value_unsafe() const& noexcept { + return internal::simdjson_result_base::value_unsafe(); +} - template - simdjson_inline T&& simdjson_result::value_unsafe() && noexcept { - return std::forward>(*this).value_unsafe(); - } +template +simdjson_inline T&& simdjson_result::value_unsafe() && noexcept { + return std::forward>(*this).value_unsafe(); +} - template - simdjson_inline simdjson_result::simdjson_result(T&& value, error_code error) noexcept - : internal::simdjson_result_base(std::forward(value), error) {} - template - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : internal::simdjson_result_base(error) {} - template - simdjson_inline simdjson_result::simdjson_result(T&& value) noexcept - : internal::simdjson_result_base(std::forward(value)) {} - template - simdjson_inline simdjson_result::simdjson_result() noexcept - : internal::simdjson_result_base() {} +template +simdjson_inline simdjson_result::simdjson_result(T &&value, error_code error) noexcept + : internal::simdjson_result_base(std::forward(value), error) {} +template +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : internal::simdjson_result_base(error) {} +template +simdjson_inline simdjson_result::simdjson_result(T &&value) noexcept + : internal::simdjson_result_base(std::forward(value)) {} +template +simdjson_inline simdjson_result::simdjson_result() noexcept + : internal::simdjson_result_base() {} } // namespace simdjson @@ -3068,27 +3533,27 @@ namespace simdjson { #include namespace simdjson { - namespace internal { +namespace internal { - template - class atomic_ptr { - public: - atomic_ptr(T* _ptr) : ptr{ _ptr } {} +template +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 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; } + 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 ptr; - }; +private: + std::atomic ptr; +}; - } // namespace internal +} // namespace internal } // namespace simdjson #endif // SIMDJSON_INTERNAL_ATOMIC_PTR_H @@ -3104,246 +3569,246 @@ namespace simdjson { namespace simdjson { - namespace dom { - class document; - } // namespace dom +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 }; +/** +* 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}; - /** - * 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); - } +/** + * 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 { +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: +/** + * 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 + * + * 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 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. + * + * 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; + /** + * @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 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; + /** + * 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 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; + /** + * 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; + /** + * 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 structural_indexes{}; - /** Next structural index to parse */ - uint32_t next_structural_index{ 0 }; + /** 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 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 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; + /** + * 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; + /** + * 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 }; +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 }; + /** + * The maximum depth (number of nested objects and arrays) supported by this parser. + * + * Defaults to DEFAULT_MAX_DEPTH. + */ + size_t _max_depth{0}; - // 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; + // 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(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_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::capacity() const noexcept { + return _capacity; +} - simdjson_pure simdjson_inline size_t dom_parser_implementation::max_depth() const noexcept { - return _max_depth; - } +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; - } +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 internal } // namespace simdjson #endif // SIMDJSON_INTERNAL_DOM_PARSER_IMPLEMENTATION_H @@ -3353,222 +3818,222 @@ namespace simdjson { 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 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 &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; + } - /** - * 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()); - } + /** + * 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; +}; - /** - * 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: +} // namespace internal - /** - * 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 list of available implementations compiled into simdjson. + */ +extern SIMDJSON_DLLIMPORTEXPORT const internal::available_implementation_list& get_available_implementations(); - /** - * 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& 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& get_active_implementation(); +/** + * The active implementation. + * + * Automatically initialized on first use to the most advanced implementation supported by this hardware. + */ +extern SIMDJSON_DLLIMPORTEXPORT internal::atomic_ptr& get_active_implementation(); } // namespace simdjson @@ -3597,171 +4062,290 @@ namespace simdjson { namespace simdjson { - class padded_string_view; +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 { +/** + * 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; + /** + * 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; + 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; + /** + * 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 size() const noexcept; - /** - * The length of the string. - * - * Does not include padding. - */ - size_t length() 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(static_cast(data_ptr)); } + /** + * The string data. + **/ + const char *data() const noexcept; + const uint8_t *u8data() const noexcept { return static_cast(static_cast(data_ptr));} - /** - * The string data. - **/ - char* data() noexcept; + /** + * The string data. + **/ + char *data() noexcept; - /** - * Create a std::string_view with the same content. - */ - operator std::string_view() const; + /** + * 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 padded_string_view with the same content. - */ - operator padded_string_view() const noexcept; + /** + * Create a std::string_view with the same content. + */ + operator std::string_view() const; - /** - * 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 load(std::string_view path) noexcept; + /** + * Create a padded_string_view with the same content. + */ + operator padded_string_view() const noexcept; - private: - padded_string& operator=(const padded_string& o) = delete; - padded_string(const padded_string& o) = delete; + /** + * 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 load(std::string_view path) noexcept; - size_t viable_size{ 0 }; - char* data_ptr{ nullptr }; + #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 load(std::wstring_view path) noexcept; + #endif - }; // padded_string +private: + friend class padded_string_builder; + padded_string &operator=(const padded_string &o) = delete; + padded_string(const padded_string &o) = delete; - /** - * 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(); } + 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& s) noexcept(false) { return out << s.value(); } +/** + * 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 &s) noexcept(false) { return out << s.value(); } #endif } // 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); +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); +inline simdjson::padded_string operator ""_padded(const char8_t *str, size_t len); #endif namespace simdjson { - namespace internal { +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; +// 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 internal } // namespace simdjson #endif // SIMDJSON_PADDED_STRING_H @@ -3772,21 +4356,21 @@ namespace simdjson { 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; +/** + * + * 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 @@ -3815,77 +4399,99 @@ namespace simdjson { 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; +/** + * 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; - public: - /** Create an empty padded_string_view. */ - inline padded_string_view() noexcept = default; +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; + /** + * 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; + 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 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; + /** + * 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; + /** The number of allocated bytes. */ + inline size_t capacity() 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; + /** check that the view has sufficient padding */ + inline bool has_sufficient_padding() const noexcept; - /** The amount of padding on the string (capacity() - length()) */ - inline size_t 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; - }; // padded_string_view + /** The amount of padding on the string (capacity() - length()) */ + inline size_t padding() const noexcept; + +}; // padded_string_view #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& s) noexcept(false); +/** + * 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 &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; } // namespace simdjson #endif // SIMDJSON_PADDED_STRING_VIEW_H @@ -3904,51 +4510,95 @@ namespace simdjson { 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 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(s), len, capacity) - { - } +inline padded_string_view::padded_string_view(const uint8_t* s, size_t len, size_t capacity) noexcept + : padded_string_view(reinterpret_cast(s), len, capacity) +{ +} #ifdef __cpp_char8_t - inline padded_string_view::padded_string_view(const char8_t* s, size_t len, size_t capacity) noexcept - : padded_string_view(reinterpret_cast(s), len, capacity) - { - } +inline padded_string_view::padded_string_view(const char8_t* s, size_t len, size_t capacity) noexcept + : padded_string_view(reinterpret_cast(s), len, capacity) +{ +} #endif - inline padded_string_view::padded_string_view(const std::string& s) noexcept - : std::string_view(s), _capacity(s.capacity()) - { +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 padded_string_view::padded_string_view(std::string_view s, size_t capacity) noexcept - : std::string_view(s), _capacity(capacity) - { - if (_capacity < s.length()) { _capacity = s.length(); } - } +inline size_t padded_string_view::capacity() const noexcept { return _capacity; } - inline size_t padded_string_view::capacity() const noexcept { return _capacity; } +inline size_t padded_string_view::padding() const noexcept { return capacity() - length(); } - 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; - } +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& s) noexcept(false) { return out << s.value(); } +inline std::ostream& operator<<(std::ostream& out, simdjson_result &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()); +} + + + } // namespace simdjson @@ -3956,183 +4606,372 @@ namespace simdjson { /* end file simdjson/padded_string_view-inl.h */ #include +#include namespace simdjson { - namespace internal { +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; - } +// 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 (1UL << 20)) { - return nullptr; - } + // 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() + 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 +} // 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)) { - } - 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); - } - } +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)) { +} +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(data), length); - } - } +inline padded_string::padded_string(const char8_t *data, size_t length) noexcept + : viable_size(length), data_ptr(internal::allocate_padded_buffer(length)) { + if ((data != nullptr) && (data_ptr != nullptr)) { + std::memcpy(data_ptr, reinterpret_cast(data), length); + } + 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) { - 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; - } +// 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 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 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 padded_string::~padded_string() noexcept { + delete[] data_ptr; +} - inline size_t padded_string::size() const noexcept { return viable_size; } +inline size_t padded_string::size() const noexcept { return viable_size; } - inline size_t padded_string::length() 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 const char *padded_string::data() const noexcept { return data_ptr; } - inline char* padded_string::data() noexcept { return data_ptr; } +inline char *padded_string::data() noexcept { return data_ptr; } - inline padded_string::operator std::string_view() const { return std::string_view(data(), length()); } +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 padded_string_view() const noexcept { - return padded_string_view(data(), length(), length() + SIMDJSON_PADDING); - } +inline padded_string::operator std::string_view() const simdjson_lifetime_bound { return std::string_view(data(), length()); } - inline simdjson_result padded_string::load(std::string_view filename) noexcept { - // 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(filename.data(), "rb"); - SIMDJSON_POP_DISABLE_WARNINGS +inline padded_string::operator padded_string_view() const noexcept simdjson_lifetime_bound { + return padded_string_view(data(), length(), length() + SIMDJSON_PADDING); +} - if (fp == nullptr) { - return IO_ERROR; - } +inline simdjson_result padded_string::load(std::string_view filename) noexcept { + // 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(filename.data(), "rb"); + SIMDJSON_POP_DISABLE_WARNINGS - // Get the file size - int ret; + 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); + ret = _fseeki64(fp, 0, SEEK_END); #else - ret = std::fseek(fp, 0, SEEK_END); + ret = std::fseek(fp, 0, SEEK_END); #endif // _WIN64 - if (ret < 0) { - std::fclose(fp); - return IO_ERROR; - } + 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; - } + __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; - } + 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(llen); - padded_string s(len); - if (s.data() == nullptr) { - std::fclose(fp); - return MEMALLOC; - } + // Allocate the padded_string + size_t len = static_cast(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; - } + // 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; + return s; +} + +#if defined(_WIN32) && SIMDJSON_CPLUSPLUS17 +inline simdjson_result padded_string::load(std::wstring_view filename) noexcept { + // 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(filename.data(), 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(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 { + 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; + } else { + 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; +} } // namespace simdjson -inline simdjson::padded_string operator "" _padded(const char* str, size_t len) { - return simdjson::padded_string(str, len); +inline simdjson::padded_string operator ""_padded(const char *str, size_t len) { + return simdjson::padded_string(str, len); } #ifdef __cpp_char8_t -inline simdjson::padded_string operator "" _padded(const char8_t* str, size_t len) { - return simdjson::padded_string(reinterpret_cast(str), len); +inline simdjson::padded_string operator ""_padded(const char8_t *str, size_t len) { + return simdjson::padded_string(reinterpret_cast(str), len); } #endif #endif // SIMDJSON_PADDED_STRING_INL_H @@ -4154,49 +4993,49 @@ inline simdjson::padded_string operator "" _padded(const char8_t* str, size_t le namespace simdjson { - /** - * @brief A DOM API on top of the simdjson parser. - */ - namespace dom { +/** + * @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; +/** 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; +/** + * 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; +class array; +class document; +class document_stream; +class element; +class key_value_pair; +class object; +class parser; #ifdef SIMDJSON_THREADS_ENABLED - struct stage1_worker; +struct stage1_worker; #endif // SIMDJSON_THREADS_ENABLED - } // namespace dom +} // namespace dom - namespace internal { +namespace internal { - template - class string_builder; - class tape_ref; +template +class string_builder; +class tape_ref; - } // namespace internal +} // namespace internal } // namespace simdjson @@ -4207,6 +5046,8 @@ namespace simdjson { #ifndef SIMDJSON_DOM_ARRAY_H #define SIMDJSON_DOM_ARRAY_H +#include + /* 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 */ @@ -4216,226 +5057,263 @@ namespace simdjson { /* skipped duplicate #include "simdjson/base.h" */ namespace simdjson { - namespace dom { - class document; - } // namespace dom +namespace dom { +class document; +} // namespace dom - namespace internal { +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; - 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 - 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; +/** + * 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; + 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 + 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 document this element references. */ + const dom::document *doc; - /** The index of this element on `doc.tape[]` */ - size_t json_index; - }; + /** The index of this element on `doc.tape[]` */ + size_t json_index; +}; - } // namespace internal +} // namespace internal } // namespace simdjson #endif // SIMDJSON_INTERNAL_TAPE_REF_H /* end file simdjson/internal/tape_ref.h */ namespace simdjson { - namespace dom { +namespace dom { - /** - * JSON array. - */ - class array { - public: - /** Create a new, invalid array */ - simdjson_inline array() noexcept; +/** + * 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; + 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; + /** + * 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; + 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; - }; + 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; + }; - /** - * 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 at_pointer(std::string_view json_pointer) 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 at_pointer(std::string_view json_pointer) 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 at(size_t index) 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; - template - friend class simdjson::internal::string_builder; - }; + /** + * Recursive function which processes the JSON path of each child element + */ + inline void process_json_path_of_child_elements(std::vector::iterator& current, std::vector::iterator& end, const std::string_view& path_suffix, std::vector& accumulator) const noexcept; - } // namespace dom + /** + * Adds support for JSONPath expression with wildcards '*' + */ + inline simdjson_result> at_path_with_wildcard(std::string_view json_path) const noexcept; - /** The result of a JSON conversion that may fail. */ - template<> - struct simdjson_result : public internal::simdjson_result_base { - 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 + /** + * 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 at_path(std::string_view json_path) const noexcept; - inline simdjson_result at_pointer(std::string_view json_pointer) const noexcept; - inline simdjson_result at(size_t index) 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 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& get_values(std::vector& 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; + template + friend class simdjson::internal::string_builder; +}; + + +} // namespace dom + +/** The result of a JSON conversion that may fail. */ +template<> +struct simdjson_result : public internal::simdjson_result_base { +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 at_pointer(std::string_view json_pointer) const noexcept; + inline void process_json_path_of_child_elements(std::vector::iterator& current, std::vector::iterator& end, const std::string_view& path_suffix, std::vector& accumulator) const noexcept; + inline simdjson_result> at_path_with_wildcard(std::string_view json_path) const noexcept; + inline simdjson_result at_path(std::string_view json_path) const noexcept; + inline simdjson_result at(size_t index) const noexcept; + inline std::vector& get_values(std::vector& out) const noexcept; #if SIMDJSON_EXCEPTIONS - inline dom::array::iterator begin() const noexcept(false); - inline dom::array::iterator end() const noexcept(false); - inline size_t size() const noexcept(false); + inline dom::array::iterator begin() const noexcept(false); + inline dom::array::iterator end() const noexcept(false); + inline size_t size() const noexcept(false); #endif // SIMDJSON_EXCEPTIONS - }; +}; } // namespace simdjson -#if defined(__cpp_lib_ranges) -#include - +#if SIMDJSON_SUPPORTS_RANGES namespace std { - namespace ranges { - template<> - inline constexpr bool enable_view = true; +namespace ranges { +template<> +inline constexpr bool enable_view = true; #if SIMDJSON_EXCEPTIONS - template<> - inline constexpr bool enable_view> = true; +template<> +inline constexpr bool enable_view> = true; #endif // SIMDJSON_EXCEPTIONS - } // namespace ranges +} // namespace ranges } // namespace std -#endif // defined(__cpp_lib_ranges) +#endif // SIMDJSON_SUPPORTS_RANGES #endif // SIMDJSON_DOM_ARRAY_H /* end file simdjson/dom/array.h */ @@ -4461,86 +5339,86 @@ namespace std { #include namespace simdjson { - namespace dom { +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; +/** + * 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 + /** + * 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; + /** + * 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 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 tape{}; + /** @private Structural values. */ + std::unique_ptr tape{}; - /** @private String values. - * - * Should be at least byte_capacity. - */ - std::unique_ptr 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. - * Passsing 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 String values. + * + * Should be at least byte_capacity. + */ + std::unique_ptr 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 +private: + size_t allocated_capacity{0}; + friend class parser; +}; // class document - } // namespace dom +} // namespace dom } // namespace simdjson #endif // SIMDJSON_DOM_DOCUMENT_H @@ -4548,645 +5426,663 @@ namespace simdjson { namespace simdjson { - namespace dom { +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 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 +/** + * 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 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; + /** 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 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 load(const std::string& path) & noexcept; - inline simdjson_result load(const std::string& path) && = delete; + /** + * 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 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 load(std::string_view path) & noexcept; + inline simdjson_result 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 load_into_document(document& doc, const std::string& path) & noexcept; - inline simdjson_result load_into_document(document& doc, const std::string& 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 load_into_document(document& doc, std::string_view path) & noexcept; + inline simdjson_result 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 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 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); - * - * ### 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 parse(const uint8_t* buf, size_t len, bool realloc_if_needed = true) & noexcept; - inline simdjson_result 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 parse(const char* buf, size_t len, bool realloc_if_needed = true) & noexcept; - simdjson_inline simdjson_result parse(const char* 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 parse(const std::string& s) & noexcept; - simdjson_inline simdjson_result parse(const std::string& s) && = delete; - /** @overload parse(const uint8_t *buf, size_t len, bool realloc_if_needed) */ - simdjson_inline simdjson_result parse(const padded_string& s) & noexcept; - simdjson_inline simdjson_result parse(const padded_string& s) && = delete; - /** @overload parse(const uint8_t *buf, size_t len, bool realloc_if_needed) */ - simdjson_inline simdjson_result parse(const padded_string_view& v) & noexcept; - simdjson_inline simdjson_result parse(const padded_string_view& v) && = 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 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 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 parse(const uint8_t *buf, size_t len, bool realloc_if_needed = true) & noexcept; + inline simdjson_result 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 parse(const char *buf, size_t len, bool realloc_if_needed = true) & noexcept; + simdjson_inline simdjson_result parse(const char *buf, size_t len, bool realloc_if_needed = true) && =delete; + /** @overload parse(const std::string &) */ + simdjson_inline simdjson_result parse(const std::string &s) & noexcept; + simdjson_inline simdjson_result parse(const std::string &s) && =delete; + /** @overload parse(const uint8_t *buf, size_t len, bool realloc_if_needed) */ + simdjson_inline simdjson_result parse(const padded_string &s) & noexcept; + simdjson_inline simdjson_result parse(const padded_string &s) && =delete; + /** @overload parse(const uint8_t *buf, size_t len, bool realloc_if_needed) */ + simdjson_inline simdjson_result parse(const padded_string_view &v) & noexcept; + simdjson_inline simdjson_result 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 parse(const char* buf) noexcept = delete; + /** @private We do not want to allow implicit conversion from C string to std::string. */ + simdjson_inline simdjson_result parse(const char *buf) noexcept = 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 parse_into_document(document& doc, const uint8_t* buf, size_t len, bool realloc_if_needed = true) & noexcept; - inline simdjson_result 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 parse_into_document(document& doc, const char* buf, size_t len, bool realloc_if_needed = true) & noexcept; - simdjson_inline simdjson_result 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 parse_into_document(document& doc, const std::string& s) & noexcept; - simdjson_inline simdjson_result 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 parse_into_document(document& doc, const padded_string& s) & noexcept; - simdjson_inline simdjson_result parse_into_document(document& doc, const padded_string& s) && = 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 parse_into_document(document& doc, const uint8_t *buf, size_t len, bool realloc_if_needed = true) & noexcept; + inline simdjson_result 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 parse_into_document(document& doc, const char *buf, size_t len, bool realloc_if_needed = true) & noexcept; + simdjson_inline simdjson_result 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 parse_into_document(document& doc, const std::string &s) & noexcept; + simdjson_inline simdjson_result 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 parse_into_document(document& doc, const padded_string &s) & noexcept; + simdjson_inline simdjson_result 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 parse_into_document(document& doc, const char* buf) noexcept = delete; + /** @private We do not want to allow implicit conversion from C string to std::string. */ + simdjson_inline simdjson_result 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 excesively 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 load_many(const std::string& path, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept; + /** + * 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 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 excesively 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 10MB, 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 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 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 parse_many(const std::string& s, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept; - inline simdjson_result parse_many(const std::string&& s, size_t batch_size) = delete;// unsafe - /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ - inline simdjson_result parse_many(const padded_string& s, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept; - inline simdjson_result parse_many(const padded_string&& s, size_t batch_size) = delete;// unsafe + /** + * 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 10MB, 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 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 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 parse_many(const std::string &s, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept; + inline simdjson_result parse_many(const std::string &&s, size_t batch_size) = delete;// unsafe + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result parse_many(const padded_string &s, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept; + inline simdjson_result parse_many(const padded_string &&s, size_t batch_size) = delete;// unsafe - /** @private We do not want to allow implicit conversion from C string to std::string. */ - simdjson_result parse_many(const char* buf, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept = delete; + /** @private We do not want to allow implicit conversion from C string to std::string. */ + simdjson_result parse_many(const char *buf, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept = delete; - /** - * 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; + /** + * 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; + /** + * @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 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 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; + /** + * 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; + /** + * 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. - */ - bool threaded{ true }; + /** + * 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 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 implementation{}; + /** @private [for benchmarking access] The implementation to use */ + std::unique_ptr implementation{}; - /** @private Use `if (parser.parse(...).error())` instead */ - bool valid{ false }; - /** @private Use `parser.parse(...).error()` instead */ - error_code error{ UNINITIALIZED }; + /** @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 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 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 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 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 */ + [[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; + /** @private Private and deprecated: use `parser.parse(...).doc.dump_raw_tape()` instead */ + inline bool dump_raw_tape(std::ostream &os) const noexcept; - private: - /** - * The maximum document length this parser will automatically support. - * - * The parser will not be automatically allocated above this amount. - */ - size_t _max_capacity; +private: + /** + * The maximum document length this parser will automatically support. + * + * The parser will not be automatically allocated above this amount. + */ + size_t _max_capacity; - /** - * The loaded buffer (reused each time load() is called) - */ - std::unique_ptr loaded_bytes; + /** + * The loaded buffer (reused each time load() is called) + */ + std::unique_ptr loaded_bytes; - /** Capacity of loaded_bytes buffer. */ - size_t _loaded_bytes_capacity{ 0 }; + /** 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 - // - // + // 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; + /** + * 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 read_file(const std::string& path) noexcept; + /** Read the file into loaded_bytes */ + inline simdjson_result read_file(std::string_view path) noexcept; - friend class parser::Iterator; - friend class document_stream; + friend class parser::Iterator; + friend class document_stream; - }; // class parser +}; // class parser - } // namespace dom +} // namespace dom } // namespace simdjson #endif // SIMDJSON_DOM_PARSER_H @@ -5199,311 +6095,311 @@ namespace simdjson { #endif namespace simdjson { - namespace dom { +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 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: +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(); + /** + * 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 }; + 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{}; - }; + /** + * 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. - * - * ```c++ - * 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; +/** + * 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(); - * - */ - inline size_t truncated_bytes() const noexcept; - /** - * An iterator through a forward-only stream of documents. - */ - class iterator { - public: - using value_type = simdjson_result; - using reference = value_type; + 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(); + * + */ + inline size_t truncated_bytes() const noexcept; + /** + * An iterator through a forward-only stream of documents. + */ + class iterator { + public: + using value_type = simdjson_result; + using reference = value_type; - using difference_type = std::ptrdiff_t; + using difference_type = std::ptrdiff_t; - using iterator_category = std::input_iterator_tag; + 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; + /** + * 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; - }; + 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; + /** + * 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: +private: - document_stream& operator=(const document_stream&) = delete; // Disallow copying - document_stream(const document_stream& other) = delete; // Disallow copying + 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) - */ - simdjson_inline document_stream( - dom::parser& parser, - const uint8_t* buf, - size_t len, - size_t batch_size - ) noexcept; + /** + * 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) + */ + simdjson_inline document_stream( + dom::parser &parser, + const uint8_t *buf, + size_t len, + size_t batch_size + ) noexcept; - /** - * Parse the first document in the buffer. Used by begin(), to handle allocation and - * initialization. - */ - inline void start() 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; + /** + * 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; + /** + * 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; + /** 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; + /** 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 error (or lack thereof) from the current document. */ - error_code error; - size_t batch_start{ 0 }; - size_t doc_index{}; + dom::parser *parser; + const uint8_t *buf; + size_t len; + size_t batch_size; + /** 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; + /** 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; + inline void load_from_stage1_thread() noexcept; - /** Start a thread to run stage 1 on the next batch. */ - inline void start_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; + /** 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 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{}; + /** 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 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; - friend struct internal::simdjson_result_base; + friend class dom::parser; + friend struct simdjson_result; + friend struct internal::simdjson_result_base; - }; // class document_stream +}; // class document_stream - } // namespace dom +} // namespace dom - template<> - struct simdjson_result : public internal::simdjson_result_base { - 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 +template<> +struct simdjson_result : public internal::simdjson_result_base { +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); + 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; + [[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 +}; // struct simdjson_result } // namespace simdjson @@ -5515,551 +6411,575 @@ namespace simdjson { #ifndef SIMDJSON_DOM_ELEMENT_H #define SIMDJSON_DOM_ELEMENT_H +#include + /* skipped duplicate #include "simdjson/dom/base.h" */ /* skipped duplicate #include "simdjson/dom/array.h" */ namespace simdjson { - namespace dom { +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 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 +}; - /** - * 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; +/** + * 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; + /** 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 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 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 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 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 get_string() const noexcept; - /** - * 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 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 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 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 get_bool() 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 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 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 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 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 get_string() const noexcept; + /** + * 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 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 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 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 get_bool() const noexcept; - /** - * Whether this element is a json array. - * - * Equivalent to is(). - */ - inline bool is_array() const noexcept; - /** - * Whether this element is a json object. - * - * Equivalent to is(). - */ - inline bool is_object() const noexcept; - /** - * Whether this element is a json string. - * - * Equivalent to is() or is(). - */ - inline bool is_string() const noexcept; - /** - * Whether this element is a json number that fits in a signed 64-bit integer. - * - * Equivalent to is(). - */ - inline bool is_int64() const noexcept; - /** - * Whether this element is a json number that fits in an unsigned 64-bit integer. - * - * Equivalent to is(). - */ - inline bool is_uint64() const noexcept; - /** - * Whether this element is a json number that fits in a double. - * - * Equivalent to is(). - */ - inline bool is_double() const noexcept; + /** + * Whether this element is a json array. + * + * Equivalent to is(). + */ + inline bool is_array() const noexcept; + /** + * Whether this element is a json object. + * + * Equivalent to is(). + */ + inline bool is_object() const noexcept; + /** + * Whether this element is a json string. + * + * Equivalent to is() or is(). + */ + inline bool is_string() const noexcept; + /** + * Whether this element is a json number that fits in a signed 64-bit integer. + * + * Equivalent to is(). + */ + inline bool is_int64() const noexcept; + /** + * Whether this element is a json number that fits in an unsigned 64-bit integer. + * + * Equivalent to is(). + */ + inline bool is_uint64() const noexcept; + /** + * Whether this element is a json number that fits in a double. + * + * Equivalent to is(). + */ + 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 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(). - */ - inline bool is_bool() const noexcept; - /** - * Whether this element is a json `null`. - */ - inline bool is_null() const noexcept; + /** + * Whether this element is a json `true` or `false`. + * + * Equivalent to is(). + */ + inline bool is_bool() const noexcept; + /** + * Whether this element is a json `null`. + */ + inline bool is_null() 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 * - * - Array: dom::array - * - Object: dom::object - * - * @tparam T bool, double, uint64_t, int64_t, std::string_view, const char *, dom::array, dom::object - */ - template - simdjson_inline bool is() 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 * + * - Array: dom::array + * - Object: dom::object + * + * @tparam T bool, double, uint64_t, int64_t, std::string_view, const char *, dom::array, dom::object + */ + template + 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 * - * - 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. - */ + /** + * Get the value as the provided type (T). + * + * Supported types: + * - Boolean: bool + * - Number: double, uint64_t, int64_t + * - String: std::string_view, const char * + * - 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 - inline simdjson_result 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."); - } + template + inline simdjson_result 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 * - * - 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 - simdjson_warn_unused simdjson_inline error_code get(T& value) 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 * + * - 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 + 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 * - * - 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 - inline void tie(T& value, error_code& error) && 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 * + * - 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 + 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 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. + * + * 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 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); + /** + * 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 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); + /** + * 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 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 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 operator[](const char* 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 operator[](const char *key) const noexcept; + simdjson_result 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 at_pointer(const std::string_view json_pointer) const noexcept; + + /** + * 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 at_pointer(const std::string_view json_pointer) const noexcept; + + inline simdjson_result> 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 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 at(const std::string_view json_pointer) const noexcept; + /** + * + * 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 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 at(size_t index) const noexcept; + /** + * 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 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 at_key(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 + */ + inline simdjson_result 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 at_key_case_insensitive(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 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< 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; + /** + * 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 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; - template - friend class simdjson::internal::string_builder; +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; + template + friend class simdjson::internal::string_builder; - }; +}; - } // namespace dom +} // namespace dom - /** The result of a JSON navigation that may fail. */ - template<> - struct simdjson_result : public internal::simdjson_result_base { - 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 +/** The result of a JSON navigation that may fail. */ +template<> +struct simdjson_result : public internal::simdjson_result_base { +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 type() const noexcept; - template - simdjson_inline bool is() const noexcept; - template - simdjson_inline simdjson_result get() const noexcept; - template - simdjson_warn_unused simdjson_inline error_code get(T& value) const noexcept; + simdjson_inline simdjson_result type() const noexcept; + template + simdjson_inline bool is() const noexcept; + template + simdjson_inline simdjson_result get() const noexcept; + template + simdjson_warn_unused simdjson_inline error_code get(T &value) const noexcept; - simdjson_inline simdjson_result get_array() const noexcept; - simdjson_inline simdjson_result get_object() const noexcept; - simdjson_inline simdjson_result get_c_str() const noexcept; - simdjson_inline simdjson_result get_string_length() const noexcept; - simdjson_inline simdjson_result get_string() const noexcept; - simdjson_inline simdjson_result get_int64() const noexcept; - simdjson_inline simdjson_result get_uint64() const noexcept; - simdjson_inline simdjson_result get_double() const noexcept; - simdjson_inline simdjson_result get_bool() const noexcept; + simdjson_inline simdjson_result get_array() const noexcept; + simdjson_inline simdjson_result get_object() const noexcept; + simdjson_inline simdjson_result get_c_str() const noexcept; + simdjson_inline simdjson_result get_string_length() const noexcept; + simdjson_inline simdjson_result get_string() const noexcept; + simdjson_inline simdjson_result get_int64() const noexcept; + simdjson_inline simdjson_result get_uint64() const noexcept; + simdjson_inline simdjson_result get_double() const noexcept; + simdjson_inline simdjson_result get_bool() 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_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 simdjson_result operator[](std::string_view key) const noexcept; - simdjson_inline simdjson_result operator[](const char* key) const noexcept; - simdjson_inline simdjson_result at_pointer(const std::string_view json_pointer) const noexcept; - [[deprecated("For standard compliance, use at_pointer instead, and prefix your pointers with a slash '/', see RFC6901 ")]] - simdjson_inline simdjson_result at(const std::string_view json_pointer) const noexcept; - simdjson_inline simdjson_result at(size_t index) const noexcept; - simdjson_inline simdjson_result at_key(std::string_view key) const noexcept; - simdjson_inline simdjson_result at_key_case_insensitive(std::string_view key) const noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) const noexcept; + simdjson_inline simdjson_result operator[](const char *key) const noexcept; + simdjson_result operator[](int) const noexcept = delete; + simdjson_inline simdjson_result at_pointer(const std::string_view json_pointer) const noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(const std::string_view json_path) const noexcept; + simdjson_inline simdjson_result 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 at(const std::string_view json_pointer) const noexcept; + simdjson_inline simdjson_result at(size_t index) const noexcept; + simdjson_inline simdjson_result at_key(std::string_view key) const noexcept; + simdjson_inline simdjson_result 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 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); + simdjson_inline dom::array::iterator begin() const noexcept(false); + simdjson_inline dom::array::iterator end() const noexcept(false); #endif // SIMDJSON_EXCEPTIONS - }; +}; } // namespace simdjson @@ -6070,275 +6990,314 @@ namespace simdjson { #ifndef SIMDJSON_DOM_OBJECT_H #define SIMDJSON_DOM_OBJECT_H +#include + /* 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 { +namespace dom { - /** - * JSON object. - */ - class object { - public: - /** Create a new, invalid object */ - simdjson_inline object() noexcept; +/** + * 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; + 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; + /** + * 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; + 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; + 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; + internal::tape_ref tape{}; - friend class object; - }; + 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 operator[](std::string_view key) const noexcept; + /** + * 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 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 operator[](const char* 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 operator[](const char *key) const noexcept; + simdjson_result 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 at_pointer(std::string_view json_pointer) 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; + * 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 at_pointer(std::string_view json_pointer) 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 at_key(std::string_view key) const noexcept; + /** + * Recursive function which processes the JSON path of each child element + */ + inline void process_json_path_of_child_elements(std::vector::iterator& current, std::vector::iterator& end, const std::string_view& path_suffix, std::vector& accumulator) 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 at_key_case_insensitive(std::string_view key) const noexcept; + /** + * Adds support for JSONPath expression with wildcards '*' + */ + inline simdjson_result> at_path_with_wildcard(std::string_view json_path) const noexcept; - /** - * Implicitly convert object to element - */ - inline operator element() 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 at_path(std::string_view json_path) const noexcept; - private: - simdjson_inline object(const internal::tape_ref& tape) 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 at_key(std::string_view key) const noexcept; - internal::tape_ref tape; + /** + * 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& get_values(std::vector& out) const noexcept; - friend class element; - friend struct simdjson_result; - template - friend class simdjson::internal::string_builder; - }; + /** + * 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 at_key_case_insensitive(std::string_view key) const noexcept; - /** - * 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; + /** + * Implicitly convert object to element + */ + inline operator element() const noexcept; - private: - simdjson_inline key_value_pair(std::string_view _key, element _value) noexcept; - friend class object; - }; +private: + simdjson_inline object(const internal::tape_ref &tape) noexcept; - } // namespace dom + internal::tape_ref tape{}; - /** The result of a JSON conversion that may fail. */ - template<> - struct simdjson_result : public internal::simdjson_result_base { - 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 + friend class element; + friend struct simdjson_result; + template + friend class simdjson::internal::string_builder; +}; - inline simdjson_result operator[](std::string_view key) const noexcept; - inline simdjson_result operator[](const char* key) const noexcept; - inline simdjson_result at_pointer(std::string_view json_pointer) const noexcept; - inline simdjson_result at_key(std::string_view key) const noexcept; - inline simdjson_result at_key_case_insensitive(std::string_view key) const noexcept; +/** + * 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 : public internal::simdjson_result_base { +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 operator[](std::string_view key) const noexcept; + inline simdjson_result operator[](const char *key) const noexcept; + simdjson_result operator[](int) const noexcept = delete; + inline simdjson_result at_pointer(std::string_view json_pointer) const noexcept; + inline void process_json_path_of_child_elements(std::vector::iterator& current, std::vector::iterator& end, const std::string_view& path_suffix, std::vector& accumulator) const noexcept; + inline simdjson_result> at_path_with_wildcard(std::string_view json_path_new) const noexcept; + inline simdjson_result at_path(std::string_view json_path) const noexcept; + inline simdjson_result at_key(std::string_view key) const noexcept; + inline std::vector& get_values(std::vector& out) const noexcept; + inline simdjson_result 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); + 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 defined(__cpp_lib_ranges) -#include - +#if SIMDJSON_SUPPORTS_RANGES namespace std { - namespace ranges { - template<> - inline constexpr bool enable_view = true; +namespace ranges { +template<> +inline constexpr bool enable_view = true; #if SIMDJSON_EXCEPTIONS - template<> - inline constexpr bool enable_view> = true; +template<> +inline constexpr bool enable_view> = true; #endif // SIMDJSON_EXCEPTIONS - } // namespace ranges +} // namespace ranges } // namespace std -#endif // defined(__cpp_lib_ranges) +#endif // SIMDJSON_SUPPORTS_RANGES #endif // SIMDJSON_DOM_OBJECT_H /* end file simdjson/dom/object.h */ @@ -6352,260 +7311,483 @@ namespace std { /* skipped duplicate #include "simdjson/dom/element.h" */ /* skipped duplicate #include "simdjson/dom/object.h" */ -#include - 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 { +/** + * 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 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; +template 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 one character **/ + simdjson_inline void one_char(char c); - simdjson_inline void call_print_newline() { - static_cast(this)->print_newline(); - } + /** Prints characters in [begin, end) verbatim. **/ + simdjson_inline void chars(const char *begin, const char *end); - simdjson_inline void call_print_indents(size_t depth) { - static_cast(this)->print_indents(depth); - } + simdjson_inline void call_print_newline() { + static_cast(this)->print_newline(); + } - simdjson_inline void call_print_space() { - static_cast(this)->print_space(); - } + simdjson_inline void call_print_indents(size_t depth) { + static_cast(this)->print_indents(depth); + } - protected: - // implementation details (subject to change) - /** Backing buffer **/ - std::vector buffer{}; // not ideal! - }; + simdjson_inline void call_print_space() { + static_cast(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(); } - /** - * @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 { - public: - simdjson_inline void print_newline(); + vector_with_small_buffer(const vector_with_small_buffer &) = delete; + vector_with_small_buffer & + operator=(const vector_with_small_buffer &) = delete; - simdjson_inline void print_indents(size_t depth); - - simdjson_inline void print_space(); - }; - - class pretty_formatter : public base_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. - */ - template - 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{}; - }; - - } // 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 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 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 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 - 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()); + void clear() { + size = 0; + capacity = StaticCapacity; + free_buffer(); + buffer = array; } -#if SIMDJSON_EXCEPTIONS - template - std::string to_string(simdjson_result x) { - if (x.error()) { throw simdjson_error(x.error()); } - return to_string(x.value()); + + 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 { +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 { +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 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 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 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 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 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 std::string to_string(simdjson_result 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 - std::string minify(T x) { - return to_string(x); - } +/** + * 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 std::string minify(T x) { return to_string(x); } #if SIMDJSON_EXCEPTIONS - template - std::string minify(simdjson_result x) { - if (x.error()) { throw simdjson_error(x.error()); } - return to_string(x.value()); - } +template std::string minify(simdjson_result 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 - std::string prettify(T x) { - simdjson::internal::string_builder sb; - sb.append(x); - std::string_view answer = sb.str(); - return std::string(answer.data(), answer.size()); - } +/** + * 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 std::string prettify(T x) { + 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 - std::string prettify(simdjson_result x) { - if (x.error()) { throw simdjson_error(x.error()); } - return to_string(x.value()); - } +template std::string prettify(simdjson_result x) { + if (x.error()) { + throw simdjson_error(x.error()); + } + return to_string(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 { + +/** + * 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 length for inlined elements (default: 80). + * Simple arrays/objects shorter than this may be rendered inline. + */ + size_t max_inline_length = 80; + + /** + * 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: 1). + * Arrays with elements of this complexity or less may have multiple + * items per line. + */ + size_t max_compact_array_complexity = 1; + + /** + * Number of spaces per indentation level (default: 4). + */ + size_t indent_spaces = 4; + + /** + * Enable tabular formatting for arrays of similar objects (default: true). + * When enabled, arrays of objects with identical keys are formatted + * as aligned tables. + */ + bool enable_table_format = true; + + /** + * Minimum number of rows to trigger table mode (default: 3). + */ + size_t min_table_rows = 3; + + /** + * Similarity threshold for table detection (default: 0.8). + * Objects must share at least this fraction of keys to be formatted + * as a table. + */ + double table_similarity_threshold = 0.8; + + /** + * Enable compact multiline arrays (default: true). + * When enabled, arrays of simple elements may have multiple items + * per line. + */ + bool enable_compact_multiline = true; + + /** + * Maximum array items per line in compact mode (default: 10). + */ + size_t max_items_per_line = 10; + + /** + * Add space inside brackets for simple containers (default: true). + * When true: { "key": "value" } + * When false: {"key": "value"} + */ + bool simple_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; +}; + +/** + * 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 +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 +std::string fractured_json(T x, const fractured_json_options& options); + +#if SIMDJSON_EXCEPTIONS +template +std::string fractured_json(simdjson_result x); + +template +std::string fractured_json(simdjson_result 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" */ @@ -6619,6 +7801,118 @@ namespace simdjson { /* 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 + +#include +/* skipped duplicate #include "simdjson/common_defs.h" */ + +#include + +namespace simdjson { +/** + * 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 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] == '"')) { + i += 2; + 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); + + i += 2; + } 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; + } + + + return std::make_pair(key, json_path.substr(i)); +} + +} // 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 @@ -6632,27 +7926,27 @@ namespace simdjson { #define SIMDJSON_INTERNAL_TAPE_TYPE_H namespace simdjson { - namespace internal { +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' - }; // enum class tape_type +/** + * 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' +}; // enum class tape_type - } // namespace internal +} // namespace internal } // namespace simdjson #endif // SIMDJSON_INTERNAL_TAPE_TYPE_H @@ -6661,111 +7955,111 @@ namespace simdjson { #include namespace simdjson { - namespace internal { +namespace internal { - constexpr const uint64_t JSON_VALUE_MASK = 0x00FFFFFFFFFFFFFF; - constexpr const uint32_t JSON_COUNT_MASK = 0xFFFFFF; +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 } {} +// +// 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_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; - } +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(doc->tape[json_index] >> 56); - } - 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); - } +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(doc->tape[json_index] >> 56); +} +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 - 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(&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; - } +template +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(&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 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(&doc->string_buf[string_buf_index + sizeof(uint32_t)]); - } +simdjson_inline const char * internal::tape_ref::get_c_str() const noexcept { + size_t string_buf_index = size_t(tape_value()); + return reinterpret_cast(&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() - ); - } +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 internal } // namespace simdjson #endif // SIMDJSON_TAPE_REF_INL_H @@ -6775,157 +8069,276 @@ namespace simdjson { namespace simdjson { - // - // simdjson_result inline implementation - // - simdjson_inline simdjson_result::simdjson_result() noexcept - : internal::simdjson_result_base() {} - simdjson_inline simdjson_result::simdjson_result(dom::array value) noexcept - : internal::simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : internal::simdjson_result_base(error) {} +// +// simdjson_result inline implementation +// +simdjson_inline simdjson_result::simdjson_result() noexcept + : internal::simdjson_result_base() {} +simdjson_inline simdjson_result::simdjson_result(dom::array value) noexcept + : internal::simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : internal::simdjson_result_base(error) {} #if SIMDJSON_EXCEPTIONS - inline dom::array::iterator simdjson_result::begin() const noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first.begin(); - } - inline dom::array::iterator simdjson_result::end() const noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first.end(); - } - inline size_t simdjson_result::size() const noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first.size(); - } +inline dom::array::iterator simdjson_result::begin() const noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first.begin(); +} +inline dom::array::iterator simdjson_result::end() const noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first.end(); +} +inline size_t simdjson_result::size() const noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first.size(); +} #endif // SIMDJSON_EXCEPTIONS - inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) const noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); +inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) const noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + + inline simdjson_result simdjson_result::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 simdjson_result> simdjson_result::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 simdjson_result::at(size_t index) const noexcept { + if (error()) { return error(); } + return first.at(index); +} + +inline std::vector& simdjson_result::get_values(std::vector& 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 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 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; } + + // Read the array index + size_t array_index = 0; + size_t i; + for (i = 0; i < json_pointer.length() && json_pointer[i] != '/'; i++) { + uint8_t digit = uint8_t(json_pointer[i] - '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; } + array_index = array_index*10 + digit; + } + + // 0 followed by other digits is invalid + if (i > 1 && json_pointer[0] == '0') { return INVALID_JSON_POINTER; } // "JSON pointer array index has other characters after 0" + + // Empty string is invalid; so is a "/" with no digits before it + if (i == 0) { return INVALID_JSON_POINTER; } // "Empty string in JSON pointer array index" + + // 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 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::iterator& current, std::vector::iterator& end, const std::string_view& path_suffix, std::vector& accumulator) const noexcept { + if (current == end) { + return; + } + + simdjson_result> result; + + + for (auto it = current; it != end; ++it) { + std::vector child_result; + auto error = it->at_path_with_wildcard(path_suffix).get(child_result); + if(error) { + continue; } - inline simdjson_result simdjson_result::at(size_t index) const noexcept { - if (error()) { return error(); } - return first.at(index); + accumulator.reserve(accumulator.size() + child_result.size()); + accumulator.insert(accumulator.end(), + std::make_move_iterator(child_result.begin()), + std::make_move_iterator(child_result.end())); + } +} + +inline simdjson_result> array::at_path_with_wildcard(std::string_view json_path) const noexcept { + SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 + + size_t i = 0; + // 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 INVALID_JSON_POINTER; + } + + if (json_path.find("*") != std::string::npos) { + std::vector child_values; + + if ( + (json_path.compare(i, 3, "[*]") == 0 && json_path.size() == i + 3) || + (json_path.compare(i, 2,".*") == 0 && json_path.size() == i + 2) + ) { + get_values(child_values); + return child_values; } - namespace dom { + std::pair key_and_json_path = get_next_key_and_json_path(json_path); - // - // 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 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 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; } + std::string_view key = key_and_json_path.first; + json_path = key_and_json_path.second; - // Read the array index - size_t array_index = 0; - size_t i; - for (i = 0; i < json_pointer.length() && json_pointer[i] != '/'; i++) { - uint8_t digit = uint8_t(json_pointer[i] - '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; } - array_index = array_index * 10 + digit; - } + if (key.size() > 0) { + if (key == "*") { + get_values(child_values); + } else { + element pointer_result; + std::string json_pointer = std::string("/") + std::string(key); + auto error = at_pointer(json_pointer).get(pointer_result); - // 0 followed by other digits is invalid - if (i > 1 && json_pointer[0] == '0') { return INVALID_JSON_POINTER; } // "JSON pointer array index has other characters after 0" + if (!error) { + child_values.emplace_back(pointer_result); + } + } - // Empty string is invalid; so is a "/" with no digits before it - if (i == 0) { return INVALID_JSON_POINTER; } // "Empty string in JSON pointer array index" + std::vector result = {}; - // 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; - } + if (child_values.size() > 0) { + std::vector::iterator child_values_begin = child_values.begin(); + std::vector::iterator child_values_end = child_values.end(); - inline simdjson_result 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; - } + process_json_path_of_child_elements(child_values_begin, child_values_end, json_path, result); + } - inline array::operator element() const noexcept { - return element(tape); - } + return result; + } else { + return INVALID_JSON_POINTER; + } + } else { + element result; + auto error = at_path(json_path).get(result); + if (error) { + return error; + } - // - // 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; - } + return std::vector{std::move(result)}; + } +} - } // namespace dom +inline simdjson_result 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& array::get_values(std::vector& 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::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 @@ -6952,738 +8365,889 @@ namespace simdjson { /* skipped duplicate #include "simdjson/dom/element-inl.h" */ /* skipped duplicate #include "simdjson/error-inl.h" */ +/* skipped duplicate #include "simdjson/jsonpathutil.h" */ #include namespace simdjson { - // - // simdjson_result inline implementation - // - simdjson_inline simdjson_result::simdjson_result() noexcept - : internal::simdjson_result_base() {} - simdjson_inline simdjson_result::simdjson_result(dom::object value) noexcept - : internal::simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : internal::simdjson_result_base(error) {} +// +// simdjson_result inline implementation +// +simdjson_inline simdjson_result::simdjson_result() noexcept + : internal::simdjson_result_base() {} +simdjson_inline simdjson_result::simdjson_result(dom::object value) noexcept + : internal::simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : internal::simdjson_result_base(error) {} - inline simdjson_result simdjson_result::operator[](std::string_view key) const noexcept { - if (error()) { return error(); } - return first[key]; - } - inline simdjson_result simdjson_result::operator[](const char* key) const noexcept { - if (error()) { return error(); } - return first[key]; - } - inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) const noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } - inline simdjson_result simdjson_result::at_key(std::string_view key) const noexcept { - if (error()) { return error(); } - return first.at_key(key); - } - inline simdjson_result simdjson_result::at_key_case_insensitive(std::string_view key) const noexcept { - if (error()) { return error(); } - return first.at_key_case_insensitive(key); - } +inline simdjson_result simdjson_result::operator[](std::string_view key) const noexcept { + if (error()) { return error(); } + return first[key]; +} +inline simdjson_result simdjson_result::operator[](const char *key) const noexcept { + if (error()) { return error(); } + return first[key]; +} +inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) const noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} +inline simdjson_result simdjson_result::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 simdjson_result> simdjson_result::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 simdjson_result::at_key(std::string_view key) const noexcept { + if (error()) { return error(); } + return first.at_key(key); +} +inline std::vector& simdjson_result::get_values(std::vector& out) const noexcept { + return first.get_values(out); +} +inline simdjson_result simdjson_result::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::begin() const noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first.begin(); - } - inline dom::object::iterator simdjson_result::end() const noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first.end(); - } - inline size_t simdjson_result::size() const noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first.size(); - } +inline dom::object::iterator simdjson_result::begin() const noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first.begin(); +} +inline dom::object::iterator simdjson_result::end() const noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first.end(); +} +inline size_t simdjson_result::size() const noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first.size(); +} #endif // SIMDJSON_EXCEPTIONS - namespace dom { +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(); - } +// +// 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 object::operator[](std::string_view key) const noexcept { - return at_key(key); - } - inline simdjson_result object::operator[](const char* key) const noexcept { - return at_key(key); - } - inline simdjson_result 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 child; +inline simdjson_result object::operator[](std::string_view key) const noexcept { + return at_key(key); +} +inline simdjson_result object::operator[](const char *key) const noexcept { + return at_key(key); +} +inline simdjson_result 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 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; - } + // 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 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; - } - // 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 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 simdjson_result 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 object::operator element() const noexcept { - return element(tape); - } +inline void object::process_json_path_of_child_elements(std::vector::iterator& current, std::vector::iterator& end, const std::string_view& path_suffix, std::vector& accumulator) const noexcept { + if (current == end) { + return; + } - // - // 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(&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)); - } + simdjson_result> result; - /** - * 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. - */ + for (auto it = current; it != end; ++it) { + std::vector child_result; + auto error = it->at_path_with_wildcard(path_suffix).get(child_result); + if(error) { + continue; + } + accumulator.reserve(accumulator.size() + child_result.size()); + accumulator.insert(accumulator.end(), + std::make_move_iterator(child_result.begin()), + std::make_move_iterator(child_result.end())); + } +} - 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 simdjson_result> object::at_path_with_wildcard(std::string_view json_path) const noexcept { + SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 + + size_t i = 0; + if (json_path.empty()) { + return INVALID_JSON_POINTER; + } + // if JSONPath starts with $, skip it + // json_path.starts_with('$') requires C++20. + if (json_path.front() == '$') { + i = 1; + } + + if (i >= json_path.size() || (json_path[i] != '.' && json_path[i] != '[')) { + // expect JSONPath expressions to always start with $ but this isn't currently + // expected in jsonpathutil.h. + return INVALID_JSON_POINTER; + } + + if (json_path.find("*") != std::string::npos) { + + std::vector child_values; + + if ( + (json_path.compare(i, 3, "[*]") == 0 && json_path.size() == i + 3) || + (json_path.compare(i, 2,".*") == 0 && json_path.size() == i + 2) + ) { + get_values(child_values); + return child_values; + } + + std::pair key_and_json_path = get_next_key_and_json_path(json_path); + + std::string_view key = key_and_json_path.first; + json_path = key_and_json_path.second; + + if (key.size() > 0) { + if (key == "*") { + get_values(child_values); + } else { + element pointer_result; + auto error = at_pointer(std::string("/") + std::string(key)).get(pointer_result); + + if (!error) { + child_values.emplace_back(pointer_result); } + } - 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) {} + std::vector result = {}; + if (child_values.size() > 0) { - } // namespace dom + std::vector::iterator child_values_begin = child_values.begin(); + std::vector::iterator child_values_end = child_values.end(); + + process_json_path_of_child_elements(child_values_begin, child_values_end, json_path, result); + } + + return result; + } else { + return INVALID_JSON_POINTER; + } + } else { + element result; + auto error = this->at_path(json_path).get(result); + if (error) { + return error; + } + return std::vector{std::move(result)}; + } +} + +inline simdjson_result 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& object::get_values(std::vector& 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 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(&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 defined(__cpp_lib_ranges) +#if SIMDJSON_SUPPORTS_RANGES static_assert(std::ranges::view); static_assert(std::ranges::sized_range); #if SIMDJSON_EXCEPTIONS static_assert(std::ranges::view>); static_assert(std::ranges::sized_range>); #endif // SIMDJSON_EXCEPTIONS -#endif // defined(__cpp_lib_ranges) +#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 #include namespace simdjson { - // - // simdjson_result inline implementation - // - simdjson_inline simdjson_result::simdjson_result() noexcept - : internal::simdjson_result_base() {} - simdjson_inline simdjson_result::simdjson_result(dom::element&& value) noexcept - : internal::simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : internal::simdjson_result_base(error) {} - inline simdjson_result simdjson_result::type() const noexcept { - if (error()) { return error(); } - return first.type(); - } +// +// simdjson_result inline implementation +// +simdjson_inline simdjson_result::simdjson_result() noexcept + : internal::simdjson_result_base() {} +simdjson_inline simdjson_result::simdjson_result(dom::element &&value) noexcept + : internal::simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : internal::simdjson_result_base(error) {} +inline simdjson_result simdjson_result::type() const noexcept { + if (error()) { return error(); } + return first.type(); +} - template - simdjson_inline bool simdjson_result::is() const noexcept { - return !error() && first.is(); - } - template - simdjson_inline simdjson_result simdjson_result::get() const noexcept { - if (error()) { return error(); } - return first.get(); - } - template - simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T& value) const noexcept { - if (error()) { return error(); } - return first.get(value); - } +template +simdjson_inline bool simdjson_result::is() const noexcept { + return !error() && first.is(); +} +template +simdjson_inline simdjson_result simdjson_result::get() const noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &value) const noexcept { + if (error()) { return error(); } + return first.get(value); +} - simdjson_inline simdjson_result simdjson_result::get_array() const noexcept { - if (error()) { return error(); } - return first.get_array(); - } - simdjson_inline simdjson_result simdjson_result::get_object() const noexcept { - if (error()) { return error(); } - return first.get_object(); - } - simdjson_inline simdjson_result simdjson_result::get_c_str() const noexcept { - if (error()) { return error(); } - return first.get_c_str(); - } - simdjson_inline simdjson_result simdjson_result::get_string_length() const noexcept { - if (error()) { return error(); } - return first.get_string_length(); - } - simdjson_inline simdjson_result simdjson_result::get_string() const noexcept { - if (error()) { return error(); } - return first.get_string(); - } - simdjson_inline simdjson_result simdjson_result::get_int64() const noexcept { - if (error()) { return error(); } - return first.get_int64(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64() const noexcept { - if (error()) { return error(); } - return first.get_uint64(); - } - simdjson_inline simdjson_result simdjson_result::get_double() const noexcept { - if (error()) { return error(); } - return first.get_double(); - } - simdjson_inline simdjson_result simdjson_result::get_bool() const noexcept { - if (error()) { return error(); } - return first.get_bool(); - } +simdjson_inline simdjson_result simdjson_result::get_array() const noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() const noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_c_str() const noexcept { + if (error()) { return error(); } + return first.get_c_str(); +} +simdjson_inline simdjson_result simdjson_result::get_string_length() const noexcept { + if (error()) { return error(); } + return first.get_string_length(); +} +simdjson_inline simdjson_result simdjson_result::get_string() const noexcept { + if (error()) { return error(); } + return first.get_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() const noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() const noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_double() const noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() const noexcept { + if (error()) { return error(); } + return first.get_bool(); +} - simdjson_inline bool simdjson_result::is_array() const noexcept { - return !error() && first.is_array(); - } - simdjson_inline bool simdjson_result::is_object() const noexcept { - return !error() && first.is_object(); - } - simdjson_inline bool simdjson_result::is_string() const noexcept { - return !error() && first.is_string(); - } - simdjson_inline bool simdjson_result::is_int64() const noexcept { - return !error() && first.is_int64(); - } - simdjson_inline bool simdjson_result::is_uint64() const noexcept { - return !error() && first.is_uint64(); - } - simdjson_inline bool simdjson_result::is_double() const noexcept { - return !error() && first.is_double(); - } - simdjson_inline bool simdjson_result::is_number() const noexcept { - return !error() && first.is_number(); - } - simdjson_inline bool simdjson_result::is_bool() const noexcept { - return !error() && first.is_bool(); - } +simdjson_inline bool simdjson_result::is_array() const noexcept { + return !error() && first.is_array(); +} +simdjson_inline bool simdjson_result::is_object() const noexcept { + return !error() && first.is_object(); +} +simdjson_inline bool simdjson_result::is_string() const noexcept { + return !error() && first.is_string(); +} +simdjson_inline bool simdjson_result::is_int64() const noexcept { + return !error() && first.is_int64(); +} +simdjson_inline bool simdjson_result::is_uint64() const noexcept { + return !error() && first.is_uint64(); +} +simdjson_inline bool simdjson_result::is_double() const noexcept { + return !error() && first.is_double(); +} +simdjson_inline bool simdjson_result::is_number() const noexcept { + return !error() && first.is_number(); +} +simdjson_inline bool simdjson_result::is_bool() const noexcept { + return !error() && first.is_bool(); +} - simdjson_inline bool simdjson_result::is_null() const noexcept { - return !error() && first.is_null(); - } +simdjson_inline bool simdjson_result::is_null() const noexcept { + return !error() && first.is_null(); +} + +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) const noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) const noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::at_pointer(const std::string_view json_pointer) const noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} +simdjson_inline simdjson_result simdjson_result::at_path(const 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); +} + +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(const std::string_view json_path) const noexcept { + if (error()) { return error(); } + return first.at_path_with_wildcard(json_path); +} - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) const noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](const char* key) const noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::at_pointer(const std::string_view json_pointer) const noexcept { - if (error()) { return error(); } - return first.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 ")]] - simdjson_inline simdjson_result simdjson_result::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 - } +[[deprecated("For standard compliance, use at_pointer instead, and prefix your pointers with a slash '/', see RFC6901 ")]] +simdjson_inline simdjson_result simdjson_result::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 simdjson_result::at(size_t index) const noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline simdjson_result simdjson_result::at_key(std::string_view key) const noexcept { - if (error()) { return error(); } - return first.at_key(key); - } - simdjson_inline simdjson_result simdjson_result::at_key_case_insensitive(std::string_view key) const noexcept { - if (error()) { return error(); } - return first.at_key_case_insensitive(key); - } +simdjson_inline simdjson_result simdjson_result::at(size_t index) const noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline simdjson_result simdjson_result::at_key(std::string_view key) const noexcept { + if (error()) { return error(); } + return first.at_key(key); +} +simdjson_inline simdjson_result simdjson_result::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::operator bool() const noexcept(false) { - return get(); - } - simdjson_inline simdjson_result::operator const char* () const noexcept(false) { - return get(); - } - simdjson_inline simdjson_result::operator std::string_view() const noexcept(false) { - return get(); - } - simdjson_inline simdjson_result::operator uint64_t() const noexcept(false) { - return get(); - } - simdjson_inline simdjson_result::operator int64_t() const noexcept(false) { - return get(); - } - simdjson_inline simdjson_result::operator double() const noexcept(false) { - return get(); - } - simdjson_inline simdjson_result::operator dom::array() const noexcept(false) { - return get(); - } - simdjson_inline simdjson_result::operator dom::object() const noexcept(false) { - return get(); - } +simdjson_inline simdjson_result::operator bool() const noexcept(false) { + return get(); +} +simdjson_inline simdjson_result::operator const char *() const noexcept(false) { + return get(); +} +simdjson_inline simdjson_result::operator std::string_view() const noexcept(false) { + return get(); +} +simdjson_inline simdjson_result::operator uint64_t() const noexcept(false) { + return get(); +} +simdjson_inline simdjson_result::operator int64_t() const noexcept(false) { + return get(); +} +simdjson_inline simdjson_result::operator double() const noexcept(false) { + return get(); +} +simdjson_inline simdjson_result::operator dom::array() const noexcept(false) { + return get(); +} +simdjson_inline simdjson_result::operator dom::object() const noexcept(false) { + return get(); +} - simdjson_inline dom::array::iterator simdjson_result::begin() const noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first.begin(); - } - simdjson_inline dom::array::iterator simdjson_result::end() const noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first.end(); - } +simdjson_inline dom::array::iterator simdjson_result::begin() const noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first.begin(); +} +simdjson_inline dom::array::iterator simdjson_result::end() const noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first.end(); +} #endif // SIMDJSON_EXCEPTIONS - namespace dom { +namespace dom { - // - // element inline implementation - // - simdjson_inline element::element() noexcept : tape{} {} - simdjson_inline element::element(const internal::tape_ref& _tape) noexcept : tape{ _tape } { } +// +// 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(tape_type); - } +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(tape_type); +} - inline simdjson_result 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 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 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 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; - } - } - inline simdjson_result 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(); - if (result < 0) { - return NUMBER_OUT_OF_RANGE; - } - return uint64_t(result); - } - return INCORRECT_TYPE; - } - return tape.next_tape_value(); - } - inline simdjson_result 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(); - // 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::max)())) { - return NUMBER_OUT_OF_RANGE; - } - return static_cast(result); - } - return INCORRECT_TYPE; - } - return tape.next_tape_value(); - } - inline simdjson_result 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 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()); - } - else if (tape.is_int64()) { - return double(tape.next_tape_value()); - } - return INCORRECT_TYPE; - } - // this is common: - return tape.next_tape_value(); - } - inline simdjson_result 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 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; - } - } +inline simdjson_result 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 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 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 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; + } +} +inline simdjson_result 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(); + if (result < 0) { + return NUMBER_OUT_OF_RANGE; + } + return uint64_t(result); + } + return INCORRECT_TYPE; + } + return tape.next_tape_value(); +} +inline simdjson_result 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(); + // 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::max)())) { + return NUMBER_OUT_OF_RANGE; + } + return static_cast(result); + } + return INCORRECT_TYPE; + } + return tape.next_tape_value(); +} +inline simdjson_result 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 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()); + } else if(tape.is_int64()) { + return double(tape.next_tape_value()); + } + return INCORRECT_TYPE; + } + // this is common: + return tape.next_tape_value(); +} +inline simdjson_result 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 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 - simdjson_warn_unused simdjson_inline error_code element::get(T& value) const noexcept { - return get().get(value); - } - // An element-specific version prevents recursion with simdjson_result::get(value) - template<> - simdjson_warn_unused simdjson_inline error_code element::get(element& value) const noexcept { - value = element(tape); - return SUCCESS; - } - template - inline void element::tie(T& value, error_code& error) && noexcept { - error = get(value); - } +template +simdjson_warn_unused simdjson_inline error_code element::get(T &value) const noexcept { + return get().get(value); +} +// An element-specific version prevents recursion with simdjson_result::get(value) +template<> +simdjson_warn_unused simdjson_inline error_code element::get(element &value) const noexcept { + value = element(tape); + return SUCCESS; +} +template +inline void element::tie(T &value, error_code &error) && noexcept { + error = get(value); +} - template - simdjson_inline bool element::is() const noexcept { - auto result = get(); - return !result.error(); - } +template +simdjson_inline bool element::is() const noexcept { + auto result = get(); + return !result.error(); +} - template<> inline simdjson_result element::get() const noexcept { return get_array(); } - template<> inline simdjson_result element::get() const noexcept { return get_object(); } - template<> inline simdjson_result element::get() const noexcept { return get_c_str(); } - template<> inline simdjson_result element::get() const noexcept { return get_string(); } - template<> inline simdjson_result element::get() const noexcept { return get_int64(); } - template<> inline simdjson_result element::get() const noexcept { return get_uint64(); } - template<> inline simdjson_result element::get() const noexcept { return get_double(); } - template<> inline simdjson_result element::get() const noexcept { return get_bool(); } +template<> inline simdjson_result element::get() const noexcept { return get_array(); } +template<> inline simdjson_result element::get() const noexcept { return get_object(); } +template<> inline simdjson_result element::get() const noexcept { return get_c_str(); } +template<> inline simdjson_result element::get() const noexcept { return get_string(); } +template<> inline simdjson_result element::get() const noexcept { return get_int64(); } +template<> inline simdjson_result element::get() const noexcept { return get_uint64(); } +template<> inline simdjson_result element::get() const noexcept { return get_double(); } +template<> inline simdjson_result element::get() const noexcept { return get_bool(); } - inline bool element::is_array() const noexcept { return is(); } - inline bool element::is_object() const noexcept { return is(); } - inline bool element::is_string() const noexcept { return is(); } - inline bool element::is_int64() const noexcept { return is(); } - inline bool element::is_uint64() const noexcept { return is(); } - inline bool element::is_double() const noexcept { return is(); } - inline bool element::is_bool() const noexcept { return is(); } - inline bool element::is_number() const noexcept { return is_int64() || is_uint64() || is_double(); } +inline bool element::is_array() const noexcept { return is(); } +inline bool element::is_object() const noexcept { return is(); } +inline bool element::is_string() const noexcept { return is(); } +inline bool element::is_int64() const noexcept { return is(); } +inline bool element::is_uint64() const noexcept { return is(); } +inline bool element::is_double() const noexcept { return is(); } +inline bool element::is_bool() const noexcept { return is(); } +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_null() const noexcept { + return tape.is_null_on_tape(); +} #if SIMDJSON_EXCEPTIONS - inline element::operator bool() const noexcept(false) { return get(); } - inline element::operator const char* () const noexcept(false) { return get(); } - inline element::operator std::string_view() const noexcept(false) { return get(); } - inline element::operator uint64_t() const noexcept(false) { return get(); } - inline element::operator int64_t() const noexcept(false) { return get(); } - inline element::operator double() const noexcept(false) { return get(); } - inline element::operator array() const noexcept(false) { return get(); } - inline element::operator object() const noexcept(false) { return get(); } +inline element::operator bool() const noexcept(false) { return get(); } +inline element::operator const char*() const noexcept(false) { return get(); } +inline element::operator std::string_view() const noexcept(false) { return get(); } +inline element::operator uint64_t() const noexcept(false) { return get(); } +inline element::operator int64_t() const noexcept(false) { return get(); } +inline element::operator double() const noexcept(false) { return get(); } +inline element::operator array() const noexcept(false) { return get(); } +inline element::operator object() const noexcept(false) { return get(); } - inline array::iterator element::begin() const noexcept(false) { - return get().begin(); - } - inline array::iterator element::end() const noexcept(false) { - return get().end(); - } +inline array::iterator element::begin() const noexcept(false) { + return get().begin(); +} +inline array::iterator element::end() const noexcept(false) { + return get().end(); +} #endif // SIMDJSON_EXCEPTIONS - inline simdjson_result element::operator[](std::string_view key) const noexcept { - return at_key(key); - } - inline simdjson_result element::operator[](const char* key) const noexcept { - return at_key(key); - } +inline simdjson_result element::operator[](std::string_view key) const noexcept { + return at_key(key); +} +inline simdjson_result 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 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::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(std::move(copy)); - } - } +inline simdjson_result 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(std::move(copy)); + } + } +} + +inline simdjson_result> 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: + return object(tape).at_path_with_wildcard(json_path); + case internal::tape_type::START_ARRAY: + return array(tape).at_path_with_wildcard(json_path); + default: + return std::vector{}; + } +} + +inline simdjson_result 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::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); - } +[[deprecated("For standard compliance, use at_pointer instead, and prefix your pointers with a slash '/', see RFC6901 ")]] +inline simdjson_result 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::at(size_t index) const noexcept { - return get().at(index); - } - inline simdjson_result element::at_key(std::string_view key) const noexcept { - return get().at_key(key); - } - inline simdjson_result element::at_key_case_insensitive(std::string_view key) const noexcept { - return get().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 simdjson_result element::at(size_t index) const noexcept { + return get().at(index); +} +inline simdjson_result element::at_key(std::string_view key) const noexcept { + return get().at_key(key); +} +inline simdjson_result element::at_key_case_insensitive(std::string_view key) const noexcept { + return get().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 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"; - default: - return out << "unexpected content!!!"; // abort() usage is forbidden in the library - } - } +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"; + default: + return out << "unexpected content!!!"; // abort() usage is forbidden in the library + } +} - } // namespace dom +} // namespace dom } // namespace simdjson #endif // SIMDJSON_ELEMENT_INL_H /* end file simdjson/dom/element-inl.h */ -#if defined(__cpp_lib_ranges) +#if SIMDJSON_SUPPORTS_RANGES static_assert(std::ranges::view); static_assert(std::ranges::sized_range); #if SIMDJSON_EXCEPTIONS static_assert(std::ranges::view>); static_assert(std::ranges::sized_range>); #endif // SIMDJSON_EXCEPTIONS -#endif // defined(__cpp_lib_ranges) +#endif // SIMDJSON_SUPPORTS_RANGES #endif // SIMDJSON_ARRAY_INL_H /* end file simdjson/dom/array-inl.h */ @@ -7714,246 +9278,244 @@ static_assert(std::ranges::sized_range /* memcmp */ namespace simdjson { - namespace dom { +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; +// +// 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::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 bool parser::dump_raw_tape(std::ostream &os) const noexcept { + return valid ? doc.dump_raw_tape(os) : false; +} - inline simdjson_result parser::read_file(const std::string& path) noexcept { - // 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.c_str(), "rb"); - SIMDJSON_POP_DISABLE_WARNINGS +inline simdjson_result parser::read_file(std::string_view path) noexcept { + // 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.data(), "rb"); + SIMDJSON_POP_DISABLE_WARNINGS - if (fp == nullptr) { - return IO_ERROR; - } + if (fp == nullptr) { + return IO_ERROR; + } - // Get the file size - int ret; + // Get the file size + int ret; #if SIMDJSON_VISUAL_STUDIO && !SIMDJSON_IS_32BITS - ret = _fseeki64(fp, 0, SEEK_END); + ret = _fseeki64(fp, 0, SEEK_END); #else - ret = std::fseek(fp, 0, SEEK_END); + ret = std::fseek(fp, 0, SEEK_END); #endif // _WIN64 - if (ret < 0) { - std::fclose(fp); - return IO_ERROR; - } + 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; - } + __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; - } + 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; - } + // 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; - } + // 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; - } + return bytes_read; +} - inline simdjson_result parser::load(const std::string& path) & noexcept { - return load_into_document(doc, path); - } +inline simdjson_result parser::load(std::string_view path) & noexcept { + return load_into_document(doc, path); +} - inline simdjson_result parser::load_into_document(document& provided_doc, const std::string& 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 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 parser::load_many(const std::string& 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(loaded_bytes.get()), len, batch_size); - } +inline simdjson_result 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(loaded_bytes.get()), len, batch_size); +} - inline simdjson_result 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(loaded_bytes.get()), buf, len); - buf = reinterpret_cast(loaded_bytes.get()); - } +inline simdjson_result 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(loaded_bytes.get()), buf, len); + buf = reinterpret_cast(loaded_bytes.get()); + } - if ((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) { - buf += 3; - len -= 3; - } - _error = implementation->parse(buf, len, provided_doc); + if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) { + buf += 3; + len -= 3; + } + _error = implementation->parse(buf, len, provided_doc); - if (_error) { return _error; } + if (_error) { return _error; } - return provided_doc.root(); - } + return provided_doc.root(); +} - simdjson_inline simdjson_result 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(buf), len, realloc_if_needed); - } - simdjson_inline simdjson_result 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 parser::parse_into_document(document& provided_doc, const padded_string& s) & noexcept { - return parse_into_document(provided_doc, s.data(), s.length(), false); - } +simdjson_inline simdjson_result 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(buf), len, realloc_if_needed); +} +simdjson_inline simdjson_result 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 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 parser::parse(const uint8_t* buf, size_t len, bool realloc_if_needed) & noexcept { - return parse_into_document(doc, buf, len, realloc_if_needed); - } +inline simdjson_result 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 parser::parse(const char* buf, size_t len, bool realloc_if_needed) & noexcept { - return parse(reinterpret_cast(buf), len, realloc_if_needed); - } - simdjson_inline simdjson_result parser::parse(const std::string& s) & noexcept { - return parse(s.data(), s.length(), s.capacity() - s.length() < SIMDJSON_PADDING); - } - simdjson_inline simdjson_result parser::parse(const padded_string& s) & noexcept { - return parse(s.data(), s.length(), false); - } - simdjson_inline simdjson_result parser::parse(const padded_string_view& v) & noexcept { - return parse(v.data(), v.length(), false); - } +simdjson_inline simdjson_result parser::parse(const char *buf, size_t len, bool realloc_if_needed) & noexcept { + return parse(reinterpret_cast(buf), len, realloc_if_needed); +} +simdjson_inline simdjson_result parser::parse(const std::string &s) & noexcept { + return parse(s.data(), s.length(), s.capacity() - s.length() < SIMDJSON_PADDING); +} +simdjson_inline simdjson_result parser::parse(const padded_string &s) & noexcept { + return parse(s.data(), s.length(), false); +} +simdjson_inline simdjson_result parser::parse(const padded_string_view &v) & noexcept { + return parse(v.data(), v.length(), false); +} - inline simdjson_result parser::parse_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); - } - inline simdjson_result parser::parse_many(const char* buf, size_t len, size_t batch_size) noexcept { - return parse_many(reinterpret_cast(buf), len, batch_size); - } - inline simdjson_result parser::parse_many(const std::string& s, size_t batch_size) noexcept { - return parse_many(s.data(), s.length(), batch_size); - } - inline simdjson_result parser::parse_many(const padded_string& s, size_t batch_size) noexcept { - return parse_many(s.data(), s.length(), batch_size); - } +inline simdjson_result parser::parse_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); +} +inline simdjson_result parser::parse_many(const char *buf, size_t len, size_t batch_size) noexcept { + return parse_many(reinterpret_cast(buf), len, batch_size); +} +inline simdjson_result parser::parse_many(const std::string &s, size_t batch_size) noexcept { + return parse_many(s.data(), s.length(), batch_size); +} +inline simdjson_result parser::parse_many(const padded_string &s, size_t batch_size) noexcept { + return parse_many(s.data(), s.length(), batch_size); +} - 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_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; - } +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); - } +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(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; - } +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; - } - } +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 dom } // namespace simdjson #endif // SIMDJSON_PARSER_INL_H @@ -7962,341 +9524,338 @@ namespace simdjson { /* skipped duplicate #include "simdjson/internal/dom_parser_implementation.h" */ namespace simdjson { - namespace dom { +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 lock(locking_mutex); - cond_var.wait(lock, [this] {return has_work == false; }); - } +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 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 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 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 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::start_thread() { + std::unique_lock 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 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 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::stop_thread() { + std::unique_lock 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 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(); - } +inline void stage1_worker::run(document_stream * ds, dom::parser * stage1, size_t next_batch_start) { + std::unique_lock 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 - ) noexcept - : parser{ &_parser }, - buf{ _buf }, - len{ _len }, - batch_size{ _batch_size <= MINIMAL_BATCH_SIZE ? MINIMAL_BATCH_SIZE : _batch_size }, - error{ SUCCESS } +simdjson_inline document_stream::document_stream( + dom::parser &_parser, + const uint8_t *_buf, + size_t _len, + size_t _batch_size +) noexcept + : parser{&_parser}, + buf{_buf}, + len{_len}, + batch_size{_batch_size <= MINIMAL_BATCH_SIZE ? MINIMAL_BATCH_SIZE : _batch_size}, + error{SUCCESS} #ifdef SIMDJSON_THREADS_ENABLED - , use_thread(_parser.threaded) // we need to make a copy because _parser.threaded can change + , 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; - } + if(worker.get() == nullptr) { + error = MEMALLOC; + } #endif - } +} - simdjson_inline document_stream::document_stream() noexcept - : parser{ nullptr }, - buf{ nullptr }, - len{ 0 }, - batch_size{ 0 }, - error{ UNINITIALIZED } +simdjson_inline document_stream::document_stream() noexcept + : parser{nullptr}, + buf{nullptr}, + len{0}, + batch_size{0}, + error{UNINITIALIZED} #ifdef SIMDJSON_THREADS_ENABLED - , use_thread(false) + , use_thread(false) #endif - { - } +{ +} - simdjson_inline document_stream::~document_stream() noexcept { +simdjson_inline document_stream::~document_stream() noexcept { #ifdef SIMDJSON_THREADS_ENABLED - worker.reset(); + worker.reset(); #endif - } +} - simdjson_inline document_stream::iterator::iterator() noexcept - : stream{ nullptr }, finished{ true } { - } +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::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 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::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::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 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; - } +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; } - // 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; } +inline void document_stream::start() noexcept { + if (error) { return; } + error = parser->ensure_capacity(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; } #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; } - } + 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(); - } + next(); +} - simdjson_inline size_t document_stream::iterator::current_index() const noexcept { - return stream->doc_index; - } +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(stream->buf) + 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(); - while (svlen > 1 && (std::isspace(start[svlen - 1]) || start[svlen - 1] == '\0')) { - svlen--; - } - return std::string_view(start, svlen); - } - } +simdjson_inline std::string_view document_stream::iterator::source() const noexcept { + const char* start = reinterpret_cast(stream->buf) + 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(); + while(svlen > 1 && (std::isspace(start[svlen-1]) || start[svlen-1] == '\0')) { + 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; } +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; } + // 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); - } + if(use_thread) { + load_from_stage1_thread(); + } else { + error = run_stage1(*parser, batch_start); + } #else - error = run_stage1(*parser, batch_start); + 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; - } + 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 { - 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::truncated_bytes() const noexcept { + 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 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; - if (remaining <= batch_size) { - return p.implementation->stage1(&buf[_batch_start], remaining, stage1_mode::streaming_final); - } - else { - return p.implementation->stage1(&buf[_batch_start], batch_size, stage1_mode::streaming_partial); - } - } +inline error_code document_stream::run_stage1(dom::parser &p, size_t _batch_start) noexcept { + size_t remaining = len - _batch_start; + if (remaining <= batch_size) { + return p.implementation->stage1(&buf[_batch_start], remaining, stage1_mode::streaming_final); + } else { + return p.implementation->stage1(&buf[_batch_start], batch_size, stage1_mode::streaming_partial); + } +} #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; } +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(); - } - } + // 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(); +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); - } + worker->run(this, & this->stage1_thread_parser, _next_batch_start); +} #endif // SIMDJSON_THREADS_ENABLED - } // namespace dom +} // namespace dom - simdjson_inline simdjson_result::simdjson_result() noexcept - : simdjson_result_base() { - } - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : simdjson_result_base(error) { - } - simdjson_inline simdjson_result::simdjson_result(dom::document_stream&& value) noexcept - : simdjson_result_base(std::forward(value)) { - } +simdjson_inline simdjson_result::simdjson_result() noexcept + : simdjson_result_base() { +} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : simdjson_result_base(error) { +} +simdjson_inline simdjson_result::simdjson_result(dom::document_stream &&value) noexcept + : simdjson_result_base(std::forward(value)) { +} #if SIMDJSON_EXCEPTIONS - simdjson_inline dom::document_stream::iterator simdjson_result::begin() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first.begin(); - } - simdjson_inline dom::document_stream::iterator simdjson_result::end() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first.end(); - } +simdjson_inline dom::document_stream::iterator simdjson_result::begin() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first.begin(); +} +simdjson_inline dom::document_stream::iterator simdjson_result::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::begin() noexcept { - first.error = error(); - return first.begin(); - } - simdjson_inline dom::document_stream::iterator simdjson_result::end() noexcept { - first.error = error(); - return first.end(); - } +simdjson_inline dom::document_stream::iterator simdjson_result::begin() noexcept { + first.error = error(); + return first.begin(); +} +simdjson_inline dom::document_stream::iterator simdjson_result::end() noexcept { + first.error = error(); + return first.end(); +} #endif // SIMDJSON_DISABLE_DEPRECATED_API #endif // SIMDJSON_EXCEPTIONS @@ -8325,59 +9884,58 @@ namespace simdjson { #include namespace simdjson { - namespace internal { +namespace internal { - inline std::ostream& operator<<(std::ostream& out, const escape_json_string& str); +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); - }; +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(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; - } +inline std::ostream& operator<<(std::ostream& out, const escape_json_string &unescaped) { + for (size_t i=0; i(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 internal } // namespace simdjson #endif // SIMDJSON_INTERNAL_JSONFORMATUTILS_H @@ -8386,149 +9944,148 @@ namespace simdjson { #include namespace simdjson { - namespace dom { +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; - } +// +// 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; - } +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; + } - // 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 - 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 - 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; - } + // 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 + 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 + 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(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(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; - 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; - } +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(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(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; + 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 dom } // namespace simdjson #endif // SIMDJSON_DOCUMENT_INL_H @@ -8544,8 +10101,8 @@ namespace simdjson { #define SIMDJSON_SERIALIZATION_INL_H /* skipped duplicate #include "simdjson/dom/base.h" */ -/* skipped duplicate #include "simdjson/dom/serialization.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" */ @@ -8555,544 +10112,1984 @@ namespace simdjson { #include 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; - } +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()); - } +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 x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); - } +inline std::ostream & +operator<<(std::ostream &out, + simdjson::simdjson_result 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()); - } +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 x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); - } +inline std::ostream & +operator<<(std::ostream &out, + simdjson::simdjson_result 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()); - } +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 x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); - } +inline std::ostream & +operator<<(std::ostream &out, + simdjson::simdjson_result x) { + if (x.error()) { + throw simdjson::simdjson_error(x.error()); + } + return (out << x.value()); +} #endif - } // namespace dom +} // 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; - } +/*** + * 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 { - } // anonymous namespace - namespace internal { +/*** + * Minifier/formatter code. + **/ - /*** - * Minifier/formatter code. - **/ +template +simdjson_inline void base_formatter::number(uint64_t x) { + char number_buffer[24]; + char *newp = fast_itoa(number_buffer, x); + chars(number_buffer, newp); +} - template - simdjson_inline void base_formatter::number(uint64_t x) { - char number_buffer[24]; - char* newp = fast_itoa(number_buffer, x); - buffer.insert(buffer.end(), number_buffer, newp); - } +template +simdjson_inline void base_formatter::number(int64_t x) { + char number_buffer[24]; + char *newp = fast_itoa(number_buffer, x); + chars(number_buffer, newp); +} - template - simdjson_inline void base_formatter::number(int64_t x) { - char number_buffer[24]; - char* newp = fast_itoa(number_buffer, x); - buffer.insert(buffer.end(), number_buffer, newp); - } +template +simdjson_inline void base_formatter::number(double x) { + char number_buffer[24]; + // Currently, passing the nullptr to the second argument is + // safe because our implementation does not check the second + // argument. + char *newp = internal::to_chars(number_buffer, nullptr, x); + chars(number_buffer, newp); +} - template - simdjson_inline void base_formatter::number(double x) { - char number_buffer[24]; - // Currently, passing the nullptr to the second argument is - // safe because our implementation does not check the second - // argument. - char* newp = internal::to_chars(number_buffer, nullptr, x); - buffer.insert(buffer.end(), number_buffer, newp); - } +template +simdjson_inline void base_formatter::start_array() { + one_char('['); +} - template - simdjson_inline void base_formatter::start_array() { one_char('['); } +template +simdjson_inline void base_formatter::end_array() { + one_char(']'); +} +template +simdjson_inline void base_formatter::start_object() { + one_char('{'); +} - template - simdjson_inline void base_formatter::end_array() { one_char(']'); } +template +simdjson_inline void base_formatter::end_object() { + one_char('}'); +} - template - simdjson_inline void base_formatter::start_object() { one_char('{'); } +template +simdjson_inline void base_formatter::comma() { + one_char(','); +} - template - simdjson_inline void base_formatter::end_object() { one_char('}'); } +template +simdjson_inline void base_formatter::true_atom() { + const char *s = "true"; + chars(s, s + 4); +} - template - simdjson_inline void base_formatter::comma() { one_char(','); } +template +simdjson_inline void base_formatter::false_atom() { + const char *s = "false"; + chars(s, s + 5); +} - template - simdjson_inline void base_formatter::true_atom() { - const char* s = "true"; - buffer.insert(buffer.end(), s, s + 4); - } +template +simdjson_inline void base_formatter::null_atom() { + const char *s = "null"; + chars(s, s + 4); +} - template - simdjson_inline void base_formatter::false_atom() { - const char* s = "false"; - buffer.insert(buffer.end(), s, s + 5); - } +template +simdjson_inline void base_formatter::one_char(char c) { + buffer.push_back(c); +} - template - simdjson_inline void base_formatter::null_atom() { - const char* s = "null"; - buffer.insert(buffer.end(), s, s + 4); - } +template +simdjson_inline void base_formatter::chars(const char *begin, + const char *end) { + buffer.append(begin, end); +} - template - simdjson_inline void base_formatter::one_char(char c) { buffer.push_back(c); } +template +simdjson_inline void +base_formatter::key(std::string_view unescaped) { + string(unescaped); + one_char(':'); +} - template - simdjson_inline void base_formatter::key(std::string_view unescaped) { - string(unescaped); - one_char(':'); - } +template +simdjson_inline void +base_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++) {} - template - simdjson_inline void base_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('\"'); +} - // At least for long strings, the following should be fast. We could - // do better by integrating the checks and the insertion. - buffer.insert(buffer.end(), 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 = "\\\""; - buffer.insert(buffer.end(), s, s + 2); - } - break; - case '\\': - { - const char* s = "\\\\"; - buffer.insert(buffer.end(), 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])]; - buffer.insert(buffer.end(), u.string, u.string + u.length); - } - else { - one_char(unescaped[i]); - } - } // switch - } // for - one_char('\"'); - } +template inline void base_formatter::clear() { + buffer.clear(); +} +template +simdjson_inline std::string_view base_formatter::str() const { + return buffer.str(); +} - template - inline void base_formatter::clear() { - buffer.clear(); - } +simdjson_inline void mini_formatter::print_newline() { return; } - template - simdjson_inline std::string_view base_formatter::str() const { - return std::string_view(buffer.data(), buffer.size()); - } +simdjson_inline void mini_formatter::print_indents(size_t depth) { + (void)depth; + return; +} - simdjson_inline void mini_formatter::print_newline() { - return; - } +simdjson_inline void mini_formatter::print_space() { return; } - simdjson_inline void mini_formatter::print_indents(size_t depth) { - (void)depth; - return; - } +simdjson_inline void pretty_formatter::print_newline() { one_char('\n'); } - simdjson_inline void mini_formatter::print_space() { - return; - } +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_newline() { - one_char('\n'); - } +simdjson_inline void pretty_formatter::print_space() { one_char(' '); } - 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(' '); - } - } +/*** + * String building code. + **/ - simdjson_inline void pretty_formatter::print_space() { - one_char(' '); - } +template +inline void string_builder::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; - /*** - * String building code. - **/ + internal::tape_ref iter(value.tape); + do { + // print commas after each value + if (after_value) { + format.comma(); + format.print_newline(); + } - template - inline void string_builder::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; + format.print_indents(depth); - internal::tape_ref iter(value.tape); - do { - // print commas after each value - if (after_value) { - format.comma(); - format.print_newline(); - } + // 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()) { - format.print_indents(depth); + // 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; + } - // 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()) { + // Output start [ + format.start_array(); + iter.json_index++; - // 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; - } + // 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; + } - // Output start [ - format.start_array(); - iter.json_index++; + is_object[depth] = false; + after_value = false; + format.print_newline(); + continue; + } - // 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; - } + // 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; + } - is_object[depth] = false; - after_value = false; - format.print_newline(); - continue; - } + // Output start { + format.start_object(); + iter.json_index++; - // 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; - } + // 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; + } - // Output start { - format.start_object(); - iter.json_index++; + is_object[depth] = true; + after_value = false; + format.print_newline(); + continue; + } - // 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; - } + // Scalars + case tape_type::STRING: + format.string(iter.get_string_view()); + break; + case tape_type::INT64: + format.number(iter.next_tape_value()); + 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()); + 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()); + 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; - is_object[depth] = true; - after_value = false; - format.print_newline(); - continue; - } + // 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; - // Scalars - case tape_type::STRING: - format.string(iter.get_string_view()); - break; - case tape_type::INT64: - format.number(iter.next_tape_value()); - 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()); - 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()); - 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; + // 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++; + } - // 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; + // Stop when we're at depth 0 + } while (depth != 0); - // 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++; - } + format.print_newline(); +} - // Stop when we're at depth 0 - } while (depth != 0); +template +inline void string_builder::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(); +} - format.print_newline(); - } +template +inline void string_builder::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 - inline void string_builder::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 +simdjson_inline void +string_builder::append(simdjson::dom::key_value_pair kv) { + format.key(kv.key); + append(kv.value); +} - template - inline void string_builder::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 +simdjson_inline void string_builder::clear() { + format.clear(); +} - template - simdjson_inline void string_builder::append(simdjson::dom::key_value_pair kv) { - format.key(kv.key); - append(kv.value); - } +template +simdjson_inline std::string_view string_builder::str() const { + return format.str(); +} - template - simdjson_inline void string_builder::clear() { - format.clear(); - } - - template - simdjson_inline std::string_view string_builder::str() const { - return format.str(); - } - - - } // namespace internal +} // 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 +#include +#include +#include + +namespace simdjson { +namespace internal { + +/** + * Layout mode for fractured JSON formatting. + */ +enum class layout_mode { + INLINE, // 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 +}; + +/** + * 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 of objects: the common keys */ + std::vector common_keys{}; + + /** Recommended layout mode based on analysis */ + layout_mode recommended_layout = layout_mode::EXPANDED; + + /** Child metrics for arrays and objects (in order of iteration) */ + std::vector children{}; +}; + +/** + * 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); + +private: + const fractured_json_options* current_opts_ = nullptr; + + /** Recursive analysis implementation */ + element_metrics analyze_element(const dom::element& elem, size_t depth); + + /** Analyze scalar values (strings, numbers, booleans, null) */ + element_metrics analyze_scalar(const dom::element& elem); + + /** Analyze an array element */ + element_metrics analyze_array(const dom::array& arr, size_t depth); + + /** Analyze an object element */ + element_metrics analyze_object(const dom::object& obj, size_t depth); + + /** 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 uniform objects suitable for table formatting. + * @param arr The array to check + * @param common_keys Output: keys common to all objects + * @return true if the array is suitable for table formatting + */ + bool check_array_uniformity(const dom::array& arr, + std::vector& common_keys) const; + + /** + * Compute similarity between two objects. + * @return Fraction of keys that are common (0.0 to 1.0) + */ + double compute_object_similarity(const dom::object& a, + const dom::object& b) const; + + /** + * Decide the recommended layout mode based on metrics and options. + */ + layout_mode decide_layout(const element_metrics& metrics, + size_t depth, + size_t available_width) 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 { +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; + + /** Set current depth for formatting decisions */ + void set_depth(size_t depth); + + /** Get current depth */ + size_t get_depth() const; + + /** Track current line length for compact multiline decisions */ + void track_line_length(size_t chars); + + /** Reset line length (after newline) */ + void reset_line_length(); + + /** Get current line length */ + size_t get_line_length() const; + + /** 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; + + // Table formatting support + /** Begin a table row */ + void begin_table_row(); + + /** End a table row */ + void end_table_row(); + + /** Set column widths for table alignment */ + void set_column_widths(const std::vector& widths); + + /** Get current column index in table mode */ + size_t get_column_index() const; + + /** Advance to next column */ + void next_column(); + + /** Add padding to align with column width */ + void align_to_column_width(size_t actual_width); + +private: + fractured_json_options options_; + layout_mode current_layout_ = layout_mode::EXPANDED; + size_t current_depth_ = 0; + size_t current_line_length_ = 0; + + // Table state + bool in_table_mode_ = false; + std::vector column_widths_; + size_t current_column_ = 0; +}; + +/** + * 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); + + /** Format an array with the appropriate layout */ + void format_array(const dom::array& arr, const element_metrics& metrics, size_t depth); + + /** 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); + + /** Format an array as a table */ + void format_array_as_table(const dom::array& arr, const element_metrics& metrics, size_t depth); + + /** 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); + + /** 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); + + /** Calculate column widths for table formatting */ + std::vector calculate_column_widths(const dom::array& arr, + const std::vector& columns) const; + + /** Measure the actual formatted length of a value (for alignment) */ + size_t measure_value_length(const dom::element& elem) const; +}; + +} // namespace internal +} // namespace simdjson + +#endif // SIMDJSON_INTERNAL_FRACTURED_FORMATTER_H +/* end file simdjson/internal/fractured_formatter.h */ + +#include +#include +#include + +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) { + 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) { + element_metrics metrics; + metrics.complexity = 0; + metrics.child_count = 0; + metrics.can_inline = true; + metrics.recommended_layout = layout_mode::INLINE; + + 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) { + 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; + + // Check if can inline + metrics.can_inline = (metrics.complexity <= current_opts_->max_inline_complexity) && + (metrics.estimated_inline_len <= current_opts_->max_inline_length); + + // Check for uniform array (table formatting) + if (current_opts_->enable_table_format && + metrics.child_count >= current_opts_->min_table_rows) { + metrics.is_uniform_array = check_array_uniformity(arr, metrics.common_keys); + } + + // Decide layout + if (metrics.child_count == 0) { + metrics.recommended_layout = layout_mode::INLINE; + } else if (metrics.can_inline) { + metrics.recommended_layout = layout_mode::INLINE; + } else if (metrics.is_uniform_array && !metrics.common_keys.empty()) { + metrics.recommended_layout = layout_mode::TABLE; + } else if (current_opts_->enable_compact_multiline && + max_child_complexity <= current_opts_->max_compact_array_complexity) { + metrics.recommended_layout = layout_mode::COMPACT_MULTILINE; + } else { + metrics.recommended_layout = layout_mode::EXPANDED; + } + + return metrics; +} + +inline element_metrics structure_analyzer::analyze_object(const dom::object& obj, + size_t depth) { + 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; + + metrics.can_inline = (metrics.complexity <= current_opts_->max_inline_complexity) && + (metrics.estimated_inline_len <= current_opts_->max_inline_length); + + // Objects use inline or expanded (no table/compact for objects) + if (metrics.child_count == 0 || metrics.can_inline) { + metrics.recommended_layout = layout_mode::INLINE; + } else { + metrics.recommended_layout = layout_mode::EXPANDED; + } + + 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(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::isnan(d) || std::isinf(d)) { + return 4; // "null" for invalid numbers + } + // 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(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 bool structure_analyzer::check_array_uniformity(const dom::array& arr, + std::vector& common_keys) const { + common_keys.clear(); + + std::set shared_keys; + dom::object first_obj; + bool have_first = false; + size_t object_count = 0; + + for (dom::element elem : arr) { + if (elem.type() != dom::element_type::OBJECT) { + return false; // Not all elements are objects + } + + dom::object obj; + if (elem.get_object().get(obj) != SUCCESS) { + return false; + } + + std::set current_keys; + for (dom::key_value_pair field : obj) { + current_keys.insert(std::string(field.key)); + } + + if (!have_first) { + shared_keys = current_keys; + first_obj = obj; + have_first = true; + } else { + // Check similarity threshold against the first object + double similarity = compute_object_similarity(first_obj, obj); + if (similarity < current_opts_->table_similarity_threshold) { + return false; // Objects are too dissimilar for table format + } + + // Intersect with current keys + std::set intersection; + std::set_intersection(shared_keys.begin(), shared_keys.end(), + current_keys.begin(), current_keys.end(), + std::inserter(intersection, intersection.begin())); + shared_keys = intersection; + } + + object_count++; + } + + if (object_count < current_opts_->min_table_rows) { + return false; + } + + // Require at least one common key for table formatting + if (shared_keys.empty()) { + return false; + } + + common_keys.assign(shared_keys.begin(), shared_keys.end()); + return true; +} + +inline double structure_analyzer::compute_object_similarity(const dom::object& a, + const dom::object& b) const { + std::set keys_a, keys_b; + for (dom::key_value_pair field : a) { + keys_a.insert(std::string(field.key)); + } + for (dom::key_value_pair field : b) { + keys_b.insert(std::string(field.key)); + } + + std::set intersection; + std::set_intersection(keys_a.begin(), keys_a.end(), + keys_b.begin(), keys_b.end(), + std::inserter(intersection, intersection.begin())); + + std::set union_set; + std::set_union(keys_a.begin(), keys_a.end(), + keys_b.begin(), keys_b.end(), + std::inserter(union_set, union_set.begin())); + + if (union_set.empty()) return 1.0; + return static_cast(intersection.size()) / static_cast(union_set.size()); +} + +inline layout_mode structure_analyzer::decide_layout(const element_metrics& metrics, + size_t depth, + size_t available_width) const { + if (metrics.child_count == 0) { + return layout_mode::INLINE; + } + + // Check inline feasibility + size_t indent_width = depth * current_opts_->indent_spaces; + if (metrics.can_inline && + metrics.estimated_inline_len + indent_width <= available_width) { + return layout_mode::INLINE; + } + + // Check table mode + if (metrics.is_uniform_array && !metrics.common_keys.empty()) { + return layout_mode::TABLE; + } + + // Check compact multiline + if (current_opts_->enable_compact_multiline && + metrics.complexity <= current_opts_->max_compact_array_complexity + 1) { + return layout_mode::COMPACT_MULTILINE; + } + + return layout_mode::EXPANDED; +} + +// +// Fractured Formatter Implementation +// + +inline fractured_formatter::fractured_formatter(const fractured_json_options& opts) + : options_(opts), column_widths_{} {} + +simdjson_inline void fractured_formatter::print_newline() { + if (current_layout_ == layout_mode::INLINE) { + 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::INLINE) { + 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::set_depth(size_t depth) { + current_depth_ = depth; +} + +inline size_t fractured_formatter::get_depth() const { + return current_depth_; +} + +inline void fractured_formatter::track_line_length(size_t chars) { + current_line_length_ += chars; +} + +inline void fractured_formatter::reset_line_length() { + current_line_length_ = 0; +} + +inline size_t fractured_formatter::get_line_length() const { + return current_line_length_; +} + +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_; +} + +inline void fractured_formatter::begin_table_row() { + in_table_mode_ = true; + current_column_ = 0; +} + +inline void fractured_formatter::end_table_row() { + in_table_mode_ = false; + current_column_ = 0; +} + +inline void fractured_formatter::set_column_widths(const std::vector& widths) { + column_widths_ = widths; +} + +inline size_t fractured_formatter::get_column_index() const { + return current_column_; +} + +inline void fractured_formatter::next_column() { + current_column_++; +} + +inline void fractured_formatter::align_to_column_width(size_t actual_width) { + if (current_column_ < column_widths_.size()) { + size_t target_width = column_widths_[current_column_]; + while (actual_width < target_width) { + one_char(' '); + actual_width++; + current_line_length_++; + } + } +} + +// +// 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) { + switch (elem.type()) { + case dom::element_type::ARRAY: { + dom::array arr; + if (elem.get_array().get(arr) == SUCCESS) { + format_array(arr, metrics, depth); + } + break; + } + case dom::element_type::OBJECT: { + dom::object obj; + if (elem.get_object().get(obj) == SUCCESS) { + format_object(obj, metrics, depth); + } + break; + } + default: + format_scalar(elem); + break; + } +} + +inline void fractured_string_builder::format_array(const dom::array& arr, + const element_metrics& metrics, + size_t depth) { + switch (metrics.recommended_layout) { + case layout_mode::INLINE: + 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) { + layout_mode prev_layout = format_.get_layout_mode(); + format_.set_layout_mode(layout_mode::INLINE); + + 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 (options_.simple_bracket_padding) { + format_.print_space(); + } + first = false; + const element_metrics& child_metrics = (child_idx < metrics.children.size()) + ? metrics.children[child_idx] : element_metrics{}; + format_element(elem, child_metrics, 0); + child_idx++; + } + + if (options_.simple_bracket_padding && !empty) { + format_.print_space(); + } + format_.end_array(); + + format_.set_layout_mode(prev_layout); +} + +inline void fractured_string_builder::format_array_compact_multiline(const dom::array& arr, + const element_metrics& metrics, + size_t depth) { + format_.start_array(); + format_.print_newline(); + format_.print_indents(depth + 1); + + size_t items_on_line = 0; + bool first = true; + size_t child_idx = 0; + + for (dom::element elem : arr) { + if (!first) { + format_.comma(); + + // Check if we should break to new line + if (items_on_line >= options_.max_items_per_line || + format_.should_break_line(20)) { // 20 is rough estimate for next item + format_.print_newline(); + format_.print_indents(depth + 1); + items_on_line = 0; + } else if (options_.comma_padding) { + format_.print_space(); + } + } + first = false; + + // Format element inline + layout_mode prev_layout = format_.get_layout_mode(); + format_.set_layout_mode(layout_mode::INLINE); + const element_metrics& child_metrics = (child_idx < metrics.children.size()) + ? metrics.children[child_idx] : element_metrics{}; + format_element(elem, child_metrics, depth + 1); + format_.set_layout_mode(prev_layout); + + items_on_line++; + child_idx++; + } + + format_.print_newline(); + format_.print_indents(depth); + format_.end_array(); +} + +inline void fractured_string_builder::format_array_as_table(const dom::array& arr, + const element_metrics& metrics, + size_t depth) { + const std::vector& columns = metrics.common_keys; + if (columns.empty()) { + format_array_expanded(arr, metrics, depth); + return; + } + + // Calculate column widths for alignment + std::vector col_widths = calculate_column_widths(arr, columns); + format_.set_column_widths(col_widths); + + 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); + format_.begin_table_row(); + + // Format object as inline with aligned columns + dom::object obj; + if (elem.get_object().get(obj) != SUCCESS) { + child_idx++; + continue; + } + + // Get child metrics for this row (object) + const element_metrics& row_metrics = (child_idx < metrics.children.size()) + ? metrics.children[child_idx] : element_metrics{}; + + format_.start_object(); + if (options_.simple_bracket_padding) { + format_.print_space(); + } + + bool first_col = true; + const size_t num_columns = columns.size(); + + for (size_t col_idx = 0; col_idx < num_columns; col_idx++) { + const std::string& key = columns[col_idx]; + const bool is_last_col = (col_idx == num_columns - 1); + + if (!first_col) { + format_.comma(); + if (options_.comma_padding) { + format_.print_space(); + } + } + first_col = false; + + // Write key + format_.key(key); + if (options_.colon_padding) { + format_.print_space(); + } + + // Find the value for this key and its metrics + dom::element value; + bool found = false; + size_t field_idx = 0; + for (dom::key_value_pair field : obj) { + if (field.key == key) { + value = field.value; + found = true; + break; + } + field_idx++; + } + + // Write value + if (found) { + layout_mode prev_layout = format_.get_layout_mode(); + format_.set_layout_mode(layout_mode::INLINE); + const element_metrics& value_metrics = (field_idx < row_metrics.children.size()) + ? row_metrics.children[field_idx] : element_metrics{}; + format_element(value, value_metrics, depth + 1); + format_.set_layout_mode(prev_layout); + } else { + format_.null_atom(); + } + + // Only pad non-last columns to align values across rows + if (!is_last_col) { + size_t actual_len = found ? measure_value_length(value) : 4; // 4 for "null" + size_t target_width = col_widths[col_idx]; + while (actual_len < target_width) { + format_.one_char(' '); + actual_len++; + } + } + + format_.next_column(); + } + + if (options_.simple_bracket_padding) { + format_.print_space(); + } + format_.end_object(); + format_.end_table_row(); + 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_idx < metrics.children.size()) + ? metrics.children[child_idx] : element_metrics{}; + format_element(elem, child_metrics, depth + 1); + 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) { + if (metrics.recommended_layout == layout_mode::INLINE || metrics.can_inline) { + 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) { + layout_mode prev_layout = format_.get_layout_mode(); + format_.set_layout_mode(layout_mode::INLINE); + + 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 (options_.simple_bracket_padding) { + format_.print_space(); + } + first = false; + + format_.key(field.key); + if (options_.colon_padding) { + format_.print_space(); + } + const element_metrics& child_metrics = (child_idx < metrics.children.size()) + ? metrics.children[child_idx] : element_metrics{}; + format_element(field.value, child_metrics, 0); + child_idx++; + } + + if (options_.simple_bracket_padding && !empty) { + format_.print_space(); + } + format_.end_object(); + + format_.set_layout_mode(prev_layout); +} + +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_idx < metrics.children.size()) + ? metrics.children[child_idx] : element_metrics{}; + format_element(field.value, child_metrics, depth + 1); + 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 size_t fractured_string_builder::measure_value_length(const dom::element& elem) const { + switch (elem.type()) { + case dom::element_type::STRING: { + std::string_view str; + if (elem.get_string().get(str) == SUCCESS) { + // Count actual escaped length + size_t len = 2; // quotes + for (char c : str) { + if (c == '"' || c == '\\' || static_cast(c) < 32) { + len += 2; // escape sequence + } else { + len += 1; + } + } + return len; + } + return 2; + } + case dom::element_type::INT64: { + int64_t val; + if (elem.get_int64().get(val) == SUCCESS) { + if (val == 0) return 1; + // Handle INT64_MIN specially to avoid overflow when negating + if (val == INT64_MIN) return 20; // "-9223372036854775808" is 20 characters + size_t len = (val < 0) ? 1 : 0; + int64_t abs_val = (val < 0) ? -val : val; + while (abs_val > 0) { len++; abs_val /= 10; } + return len; + } + return 1; + } + case dom::element_type::UINT64: { + uint64_t val; + if (elem.get_uint64().get(val) == SUCCESS) { + if (val == 0) return 1; + size_t len = 0; + while (val > 0) { len++; val /= 10; } + return len; + } + return 1; + } + case dom::element_type::DOUBLE: { + double val; + if (elem.get_double().get(val) == SUCCESS) { + char buf[32]; + int len = snprintf(buf, sizeof(buf), "%.17g", val); + return len > 0 ? static_cast(len) : 1; + } + return 1; + } + case dom::element_type::BOOL: { + bool val; + if (elem.get_bool().get(val) == SUCCESS) { + return val ? 4 : 5; // "true" or "false" + } + return 5; + } + case dom::element_type::NULL_VALUE: + return 4; // "null" + default: + return 4; + } +} + +inline std::vector fractured_string_builder::calculate_column_widths( + const dom::array& arr, + const std::vector& columns) const { + + std::vector widths(columns.size(), 0); + + for (dom::element elem : arr) { + dom::object obj; + if (elem.get_object().get(obj) != SUCCESS) { + continue; + } + + for (size_t col_idx = 0; col_idx < columns.size(); col_idx++) { + const std::string& key = columns[col_idx]; + + for (dom::key_value_pair field : obj) { + if (field.key == key) { + // Measure actual value length + size_t len = measure_value_length(field.value); + widths[col_idx] = (std::max)(widths[col_idx], len); + break; + } + } + } + } + + return widths; +} + +} // namespace internal + +// +// Public API Implementation +// + +template +std::string fractured_json(T x) { + return fractured_json(x, fractured_json_options{}); +} + +template +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 +std::string fractured_json(simdjson_result x) { + if (x.error()) { + throw simdjson_error(x.error()); + } + return fractured_json(x.value()); +} + +template +std::string fractured_json(simdjson_result x, const fractured_json_options& options) { + if (x.error()) { + throw simdjson_error(x.error()); + } + return fractured_json(x.value(), options); +} +#endif + +// Explicit template instantiations for common types +template std::string fractured_json(dom::element x); +template std::string fractured_json(dom::element x, const fractured_json_options& options); +template std::string fractured_json(dom::array x); +template std::string fractured_json(dom::array x, const fractured_json_options& options); +template std::string fractured_json(dom::object x); +template std::string fractured_json(dom::object x, const fractured_json_options& options); + +#if SIMDJSON_EXCEPTIONS +template std::string fractured_json(simdjson_result x); +template std::string fractured_json(simdjson_result x, const fractured_json_options& 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/ondemand.h: #include "simdjson/ondemand.h" */ -/* begin file simdjson/ondemand.h */ -#ifndef SIMDJSON_ONDEMAND_H -#define SIMDJSON_ONDEMAND_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/ondemand.h: #include "simdjson/builtin/ondemand.h" */ -/* begin file simdjson/builtin/ondemand.h */ -#ifndef SIMDJSON_BUILTIN_ONDEMAND_H -#define SIMDJSON_BUILTIN_ONDEMAND_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 */ @@ -9121,6 +12118,8 @@ namespace simdjson { #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) @@ -9222,22 +12221,27 @@ namespace simdjson { #endif #ifndef SIMDJSON_IMPLEMENTATION_LASX -#define SIMDJSON_IMPLEMENTATION_LASX (SIMDJSON_IS_LOONGARCH64 && __loongarch_asx) +#define SIMDJSON_IMPLEMENTATION_LASX (SIMDJSON_IS_LSX) #endif -#define SIMDJSON_CAN_ALWAYS_RUN_LASX (SIMDJSON_IMPLEMENTATION_LASX) +#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_LOONGARCH64 && __loongarch_sx) +#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 +#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 @@ -9263,6 +12267,8 @@ namespace simdjson { #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 @@ -9279,36 +12285,38 @@ namespace simdjson { namespace simdjson { #if SIMDJSON_BUILTIN_IMPLEMENTATION_IS(arm64) - namespace arm64 {} + namespace arm64 {} #elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(fallback) - namespace fallback {} + namespace fallback {} #elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(haswell) - namespace haswell {} + namespace haswell {} #elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(icelake) - namespace icelake {} + namespace icelake {} #elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(ppc64) - namespace ppc64 {} + namespace ppc64 {} #elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(westmere) - namespace westmere {} + namespace westmere {} #elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lsx) - namespace lsx {} + namespace lsx {} #elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lasx) - namespace 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; + /** + * 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 @@ -9386,31 +12394,33 @@ POSSIBILITY OF SUCH DAMAGE. #define SIMDJSON_INTERNAL_INSTRUCTION_SET_H namespace simdjson { - namespace internal { +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, - }; +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, +}; - } // namespace internal +} // namespace internal } // namespace simdjson #endif // SIMDJSON_INTERNAL_INSTRUCTION_SET_H @@ -9424,23 +12434,23 @@ namespace simdjson { /* 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); +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 +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]; +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 internal } // namespace simdjson #endif // SIMDJSON_INTERNAL_JSONCHARUTILS_TABLES_H @@ -9453,56 +12463,714 @@ namespace simdjson { /* 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; +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; - }; +/** + * 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[]; +// 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. - */ +/** + * 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. - extern SIMDJSON_DLLIMPORTEXPORT const uint64_t power_of_five_128[]; - } // namespace internal +// 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 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 @@ -9517,21 +13185,20 @@ namespace simdjson { #include namespace simdjson { // table modified and copied from - namespace internal { // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetTable +namespace internal { // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetTable - extern SIMDJSON_DLLIMPORTEXPORT const unsigned char BitsSetTable256mul2[256]; +extern SIMDJSON_DLLIMPORTEXPORT const unsigned char BitsSetTable256mul2[256]; - extern SIMDJSON_DLLIMPORTEXPORT const uint8_t pshufb_combine_table[272]; +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]; +// 256 * 8 bytes = 2kB, easily fits in cache. +extern SIMDJSON_DLLIMPORTEXPORT const uint64_t thintable_epi8[256]; - } // namespace internal +} // 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 */ @@ -9551,24 +13218,24 @@ namespace simdjson { // table modified and copied from /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { +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& 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; - }; +/** + * @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& 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 arm64 } // namespace simdjson #endif // SIMDJSON_ARM64_IMPLEMENTATION_H @@ -9585,28 +13252,28 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace fallback { +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& 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; - }; +/** + * @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& 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 fallback } // namespace simdjson #endif // SIMDJSON_FALLBACK_IMPLEMENTATION_H @@ -9625,28 +13292,28 @@ namespace simdjson { // The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_HASWELL namespace simdjson { - namespace haswell { +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& 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; - }; +/** + * @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& 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 haswell } // namespace simdjson #endif // SIMDJSON_HASWELL_IMPLEMENTATION_H @@ -9665,28 +13332,28 @@ namespace simdjson { // The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_ICELAKE namespace simdjson { - namespace icelake { +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& 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; - }; +/** + * @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& 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 icelake } // namespace simdjson #endif // SIMDJSON_ICELAKE_IMPLEMENTATION_H @@ -9705,32 +13372,32 @@ namespace simdjson { namespace simdjson { - /** - * Implementation for ALTIVEC (PPC64). - */ - namespace ppc64 { +/** + * 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) {} +/** + * @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& 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; - }; + simdjson_warn_unused error_code create_dom_parser_implementation( + size_t capacity, size_t max_length, + std::unique_ptr &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 ppc64 } // namespace simdjson #endif // SIMDJSON_PPC64_IMPLEMENTATION_H @@ -9749,24 +13416,24 @@ namespace simdjson { // The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_WESTMERE namespace simdjson { - namespace westmere { +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& 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; - }; +/** + * @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& 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 westmere } // namespace simdjson #endif // SIMDJSON_WESTMERE_IMPLEMENTATION_H @@ -9784,24 +13451,24 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { +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& 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; - }; +/** + * @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& 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 lsx } // namespace simdjson #endif // SIMDJSON_LSX_IMPLEMENTATION_H @@ -9819,28 +13486,66 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { +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& 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; - }; +/** + * @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& 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 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", + 0 + ) {} + simdjson_warn_unused error_code create_dom_parser_implementation( + size_t capacity, + size_t max_length, + std::unique_ptr& 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 @@ -9849,13 +13554,13 @@ namespace simdjson { #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(); + /** + * 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 @@ -9886,21 +13591,21 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - /** - * Implementation for NEON (ARMv8). - */ - namespace arm64 { +/** + * Implementation for NEON (ARMv8). + */ +namespace arm64 { - class implementation; +class implementation; - namespace { - namespace simd { - template struct simd8; - template struct simd8x64; - } // namespace simd - } // unnamed namespace +namespace { +namespace simd { +template struct simd8; +template struct simd8x64; +} // namespace simd +} // unnamed namespace - } // namespace arm64 +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_ARM64_BASE_H @@ -9933,100 +13638,106 @@ static_assert(sizeof(uint8x16_t) <= simdjson::SIMDJSON_PADDING, "insufficient pa /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { - namespace { +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) { -#ifdef 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; +// 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); + 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 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) { -#ifdef 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; +// 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); + 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) { - return vaddv_u8(vcnt_u8(vcreate_u8(input_num))); - } +/* result might be undefined when input_num is zero */ +simdjson_inline int count_ones(uint64_t input_num) { + return vaddv_u8(vcnt_u8(vcreate_u8(input_num))); +} #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. - */ +/** + * 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; - } +/* 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); - } +/** + * 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 - simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t* result) { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - * result = value1 + value2; - return *result < value1; +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t *result) { +#if SIMDJSON_REGULAR_VISUAL_STUDIO + *result = value1 + value2; + return *result < value1; #else - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); #endif - } +} - } // unnamed namespace - } // namespace arm64 +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_ARM64_BITMANIPULATION_H @@ -10041,39 +13752,39 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { - namespace { +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; - } +// +// 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 +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif @@ -10097,43 +13808,49 @@ namespace simdjson { #endif // SIMDJSON_REGULAR_VISUAL_STUDIO && SIMDJSON_IS_ARM64 namespace simdjson { - namespace arm64 { - namespace numberparsing { +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); - } +// 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; +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; + // 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 + 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); + __uint128_t r = (static_cast<__uint128_t>(value1)) * value2; + answer.low = uint64_t(r); + answer.high = uint64_t(r >> 64); #endif - return answer; - } + return answer; +} - } // namespace numberparsing - } // namespace arm64 +} // 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 */ @@ -10149,13 +13866,13 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { - namespace { - namespace simd { +namespace arm64 { +namespace { +namespace simd { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - namespace { - // Start of private section with Visual Studio workaround +#if SIMDJSON_REGULAR_VISUAL_STUDIO +namespace { +// Start of private section with Visual Studio workaround #ifndef simdjson_make_uint8x16_t @@ -10207,432 +13924,444 @@ namespace simdjson { #endif // End of private section with Visual Studio workaround - } // namespace +} // namespace #endif // SIMDJSON_REGULAR_VISUAL_STUDIO - template - struct simd8; + template + struct simd8; - // - // Base class of simd8 and simd8, both of which use uint8x16_t internally. - // - template> - struct base_u8 { - uint8x16_t value; - static const int SIZE = sizeof(value); + // + // Base class of simd8 and simd8, both of which use uint8x16_t internally. + // + template> + 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; } + // 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 operator|(const simd8 other) const { return vorrq_u8(*this, other); } - simdjson_inline simd8 operator&(const simd8 other) const { return vandq_u8(*this, other); } - simdjson_inline simd8 operator^(const simd8 other) const { return veorq_u8(*this, other); } - simdjson_inline simd8 bit_andnot(const simd8 other) const { return vbicq_u8(*this, other); } - simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - simdjson_inline simd8& operator|=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast | other; return *this_cast; } - simdjson_inline simd8& operator&=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast & other; return *this_cast; } - simdjson_inline simd8& operator^=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast ^ other; return *this_cast; } + // Bit operations + simdjson_inline simd8 operator|(const simd8 other) const { return vorrq_u8(*this, other); } + simdjson_inline simd8 operator&(const simd8 other) const { return vandq_u8(*this, other); } + simdjson_inline simd8 operator^(const simd8 other) const { return veorq_u8(*this, other); } + simdjson_inline simd8 bit_andnot(const simd8 other) const { return vbicq_u8(*this, other); } + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } + simdjson_inline simd8& operator|=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline simd8& operator&=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline simd8& operator^=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast ^ other; return *this_cast; } - friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return vceqq_u8(lhs, rhs); } + friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return vceqq_u8(lhs, rhs); } - template - simdjson_inline simd8 prev(const simd8 prev_chunk) const { - return vextq_u8(prev_chunk, *this, 16 - N); - } - }; + template + simdjson_inline simd8 prev(const simd8 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 : base_u8 { - typedef uint16_t bitmask_t; - typedef uint32_t bitmask2_t; + // SIMD byte mask type (returned by things like eq and gt) + template<> + struct simd8: base_u8 { + typedef uint16_t bitmask_t; + typedef uint32_t bitmask2_t; - static simdjson_inline simd8 splat(bool _value) { return vmovq_n_u8(uint8_t(-(!!_value))); } + static simdjson_inline simd8 splat(bool _value) { return vmovq_n_u8(uint8_t(-(!!_value))); } - simdjson_inline simd8(const uint8x16_t _value) : base_u8(_value) {} - // False constructor - simdjson_inline simd8() : simd8(vdupq_n_u8(0)) {} - // Splat constructor - simdjson_inline simd8(bool _value) : simd8(splat(_value)) {} + simdjson_inline simd8(const uint8x16_t _value) : base_u8(_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 { -#ifdef 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); + // 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 }; + 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); - } - simdjson_inline bool any() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) != 0; } - }; + 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); + } + simdjson_inline bool any() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) != 0; } + }; - // Unsigned bytes - template<> - struct simd8 : base_u8 { - 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); } + // Unsigned bytes + template<> + struct simd8: base_u8 { + 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(_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 -#ifdef 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 - )) {} + simdjson_inline simd8(const uint8x16_t _value) : base_u8(_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 - }) {} + 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 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( - 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 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( + 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); } + // Store to array + simdjson_inline void store(uint8_t dst[16]) const { return vst1q_u8(dst, *this); } - // Saturated math - simdjson_inline simd8 saturating_add(const simd8 other) const { return vqaddq_u8(*this, other); } - simdjson_inline simd8 saturating_sub(const simd8 other) const { return vqsubq_u8(*this, other); } + // Saturated math + simdjson_inline simd8 saturating_add(const simd8 other) const { return vqaddq_u8(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return vqsubq_u8(*this, other); } - // Addition/subtraction are the same for signed and unsigned - simdjson_inline simd8 operator+(const simd8 other) const { return vaddq_u8(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return vsubq_u8(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *this; } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *this; } + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { return vaddq_u8(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return vsubq_u8(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *this; } + simdjson_inline simd8& operator-=(const simd8 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 max_val(const simd8 other) const { return vmaxq_u8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return vminq_u8(*this, other); } - simdjson_inline simd8 operator<=(const simd8 other) const { return vcleq_u8(*this, other); } - simdjson_inline simd8 operator>=(const simd8 other) const { return vcgeq_u8(*this, other); } - simdjson_inline simd8 operator<(const simd8 other) const { return vcltq_u8(*this, other); } - simdjson_inline simd8 operator>(const simd8 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 gt_bits(const simd8 other) const { return simd8(*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 lt_bits(const simd8 other) const { return simd8(*this < other); } + // 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 max_val(const simd8 other) const { return vmaxq_u8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return vminq_u8(*this, other); } + simdjson_inline simd8 operator<=(const simd8 other) const { return vcleq_u8(*this, other); } + simdjson_inline simd8 operator>=(const simd8 other) const { return vcgeq_u8(*this, other); } + simdjson_inline simd8 operator<(const simd8 other) const { return vcltq_u8(*this, other); } + simdjson_inline simd8 operator>(const simd8 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 gt_bits(const simd8 other) const { return simd8(*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 lt_bits(const simd8 other) const { return simd8(*this < other); } - // Bit-specific operations - simdjson_inline simd8 any_bits_set(simd8 bits) const { return vtstq_u8(*this, bits); } - simdjson_inline bool any_bits_set_anywhere() const { return this->max_val() != 0; } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return (*this & bits).any_bits_set_anywhere(); } - template - simdjson_inline simd8 shr() const { return vshrq_n_u8(*this, N); } - template - simdjson_inline simd8 shl() const { return vshlq_n_u8(*this, N); } + // Bit-specific operations + simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return (*this & bits).any_bits_set_anywhere(); } + template + simdjson_inline simd8 shr() const { return vshrq_n_u8(*this, N); } + template + simdjson_inline simd8 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 - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return lookup_table.apply_lookup_16_to(*this); - } + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 lookup_table) const { + return lookup_table.apply_lookup_16_to(*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 compress(uint16_t mask) would be - // sensible, but the AVX ISA makes this kind of approach difficult. - template - 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 -#ifdef 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); + // 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 compress(uint16_t mask) would be + // sensible, but the AVX ISA makes this kind of approach difficult. + template + 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 }; + 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(pshufb_combine_table + pop1 * 8)); - uint8x16_t answer = vqtbl1q_u8(pruned, compactmask); - vst1q_u8(reinterpret_cast(output), answer); - } + 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(pshufb_combine_table + pop1 * 8)); + uint8x16_t answer = vqtbl1q_u8(pruned, compactmask); + vst1q_u8(reinterpret_cast(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 - 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 -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - uint8x8_t inc = simdjson_make_uint8x8_t(0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08); + // 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 + 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 }; + 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)); - } + 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 - simdjson_inline simd8 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::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } - template - simdjson_inline simd8 apply_lookup_16_to(const simd8 original) { - return vqtbl1q_u8(*this, simd8(original)); - } - }; + template + simdjson_inline simd8 apply_lookup_16_to(const simd8 original) { + return vqtbl1q_u8(*this, simd8(original)); + } + }; - // Signed bytes - template<> - struct simd8 { - int8x16_t value; + // Signed bytes + template<> + struct simd8 { + int8x16_t value; - static simdjson_inline simd8 splat(int8_t _value) { return vmovq_n_s8(_value); } - static simdjson_inline simd8 zero() { return vdupq_n_s8(0); } - static simdjson_inline simd8 load(const int8_t values[16]) { return vld1q_s8(values); } + static simdjson_inline simd8 splat(int8_t _value) { return vmovq_n_s8(_value); } + static simdjson_inline simd8 zero() { return vdupq_n_s8(0); } + static simdjson_inline simd8 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; } + // 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 -#ifdef 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 - )) {} + // 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 - }) {} + 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 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( - 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 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( + 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); } + // 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. + // 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)) {} + simdjson_inline explicit simd8(const uint8x16_t other): simd8(vreinterpretq_s8_u8(other)) {} #endif - simdjson_inline explicit operator simd8() const { return vreinterpretq_u8_s8(this->value); } + simdjson_inline explicit operator simd8() const { return vreinterpretq_u8_s8(this->value); } - // Math - simdjson_inline simd8 operator+(const simd8 other) const { return vaddq_s8(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return vsubq_s8(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *this; } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *this; } + // Math + simdjson_inline simd8 operator+(const simd8 other) const { return vaddq_s8(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return vsubq_s8(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *this; } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *this; } - // Order-sensitive comparisons - simdjson_inline simd8 max_val(const simd8 other) const { return vmaxq_s8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return vminq_s8(*this, other); } - simdjson_inline simd8 operator>(const simd8 other) const { return vcgtq_s8(*this, other); } - simdjson_inline simd8 operator<(const simd8 other) const { return vcltq_s8(*this, other); } - simdjson_inline simd8 operator==(const simd8 other) const { return vceqq_s8(*this, other); } + // Order-sensitive comparisons + simdjson_inline simd8 max_val(const simd8 other) const { return vmaxq_s8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return vminq_s8(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return vcgtq_s8(*this, other); } + simdjson_inline simd8 operator<(const simd8 other) const { return vcltq_s8(*this, other); } + simdjson_inline simd8 operator==(const simd8 other) const { return vceqq_s8(*this, other); } - template - simdjson_inline simd8 prev(const simd8 prev_chunk) const { - return vextq_s8(prev_chunk, *this, 16 - N); - } + template + simdjson_inline simd8 prev(const simd8 prev_chunk) const { + return vextq_s8(prev_chunk, *this, 16 - N); + } - // Perform a lookup assuming no value is larger than 16 - template - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return lookup_table.apply_lookup_16_to(*this); - } - template - simdjson_inline simd8 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::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } + // Perform a lookup assuming no value is larger than 16 + template + simdjson_inline simd8 lookup_16(simd8 lookup_table) const { + return lookup_table.apply_lookup_16_to(*this); + } + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } - template - simdjson_inline simd8 apply_lookup_16_to(const simd8 original) { - return vqtbl1q_s8(*this, simd8(original)); - } - }; + template + simdjson_inline simd8 apply_lookup_16_to(const simd8 original) { + return vqtbl1q_s8(*this, simd8(original)); + } + }; - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 4, "ARM kernel should use four registers per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 4, "ARM kernel should use four registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{ chunk0, chunk1, chunk2, chunk3 } {} - simdjson_inline simd8x64(const T ptr[64]) : chunks{ simd8::load(ptr), simd8::load(ptr + 16), simd8::load(ptr + 32), simd8::load(ptr + 48) } {} + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::load(ptr+16), simd8::load(ptr+32), simd8::load(ptr+48)} {} - simdjson_inline void store(T ptr[64]) const { - this->chunks[0].store(ptr + sizeof(simd8) * 0); - this->chunks[1].store(ptr + sizeof(simd8) * 1); - this->chunks[2].store(ptr + sizeof(simd8) * 2); - this->chunks[3].store(ptr + sizeof(simd8) * 3); - } + simdjson_inline void store(T ptr[64]) const { + this->chunks[0].store(ptr+sizeof(simd8)*0); + this->chunks[1].store(ptr+sizeof(simd8)*1); + this->chunks[2].store(ptr+sizeof(simd8)*2); + this->chunks[3].store(ptr+sizeof(simd8)*3); + } - simdjson_inline simd8 reduce_or() const { - return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]); - } + simdjson_inline simd8 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 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 { -#ifdef 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 - ); + 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 - }; + 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); - } + // 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] == mask, - this->chunks[1] == mask, - this->chunks[2] == mask, - this->chunks[3] == mask - ).to_bitmask(); - } + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] <= mask, - this->chunks[1] <= mask, - this->chunks[2] <= mask, - this->chunks[3] <= mask - ).to_bitmask(); - } - }; // struct simd8x64 + simdjson_inline uint64_t lteq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] <= mask, + this->chunks[1] <= mask, + this->chunks[2] <= mask, + this->chunks[3] <= mask + ).to_bitmask(); + } + }; // struct simd8x64 - } // namespace simd - } // unnamed namespace - } // namespace arm64 +} // namespace simd +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_ARM64_SIMD_H @@ -10649,46 +14378,72 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { - namespace { +namespace arm64 { +namespace { - using namespace simd; +using namespace simd; - // Holds backslashes and quotes locations. - struct backslash_and_quote { - public: - static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t* src, uint8_t* dst); +// 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); } + 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 + 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 v0(src); - simd8 v1(src + sizeof(v0)); - v0.store(dst); - v1.store(dst + sizeof(v0)); +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 v0(src); + simd8 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(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); - return { - uint32_t(bs_and_quote), // bs_bits - uint32_t(bs_and_quote >> 32) // quote_bits - }; - } + // 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(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); + return { + uint32_t(bs_and_quote), // bs_bits + uint32_t(bs_and_quote >> 32) // quote_bits + }; +} - } // unnamed namespace - } // namespace arm64 +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 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 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 @@ -10723,10 +14478,12 @@ namespace simdjson { /* 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_LSX */ -/* amalgamation skipped (editor-only): #include "simdjson/lsx/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 */ @@ -10736,22 +14493,22 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { +namespace arm64 { - struct open_container; - class dom_parser_implementation; +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 - }; +/** + * 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 arm64 } // namespace simdjson #endif // SIMDJSON_GENERIC_BASE_H @@ -10768,99 +14525,97 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { - namespace { - namespace jsoncharutils { +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]; - } +// 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]; - } +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; - } +// 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 - } +// 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; - } +// 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 jsoncharutils +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_GENERIC_JSONCHARUTILS_H @@ -10878,69 +14633,69 @@ namespace simdjson { #include namespace simdjson { - namespace arm64 { - namespace { - /// @private - namespace atomparsing { +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; } +// 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; } - // 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); - } +// 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); +} - 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) { + 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_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) { + 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_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) { + 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; } - } +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; } +} - } // namespace atomparsing - } // unnamed namespace - } // namespace arm64 +} // namespace atomparsing +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_GENERIC_ATOMPARSING_H @@ -10956,83 +14711,83 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { +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 +// 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"); +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_containers{}; - /** Whether each open container is a [ or { */ - std::unique_ptr 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{}; +class dom_parser_implementation final : public internal::dom_parser_implementation { +public: + /** Tape location of each open { or [ */ + std::unique_ptr open_containers{}; + /** Whether each open container is a [ or { */ + std::unique_ptr 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; + 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); + 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 arm64 } // namespace simdjson namespace simdjson { - namespace arm64 { +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; +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) { return CAPACITY; } - // Stage 1 index output - size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; - 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; +// 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) { return CAPACITY; } + // Stage 1 index output + size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; + 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; - } + _capacity = capacity; + return SUCCESS; +} - inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { - // 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; } +inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { + // 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; - } + _max_depth = max_depth; + return SUCCESS; +} - } // namespace arm64 +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H @@ -11047,129 +14802,148 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { +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 : public internal::implementation_simdjson_result_base { - * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} - * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} - * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} - * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} - * // Your extra methods here - * } - * - * Then any method returning simdjson_result will be chainable with your methods. - */ - template - struct implementation_simdjson_result_base { +// 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 : public internal::implementation_simdjson_result_base { + * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} + * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} + * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} + * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} + * // Your extra methods here + * } + * + * Then any method returning simdjson_result will be chainable with your methods. + */ +template +struct implementation_simdjson_result_base { - /** - * Create a new empty result with error = UNINITIALIZED. - */ - simdjson_inline implementation_simdjson_result_base() noexcept = default; + /** + * 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 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 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; + /** + * 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 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; + /** + * 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_inline error_code error() const 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& value() & noexcept(false); + /** + * 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); - /** - * Take the result value (move it). - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline T&& value() && 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&& take_value() && noexcept(false); + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& 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); + /** + * 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; - 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 + /** + * 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; - } // namespace arm64 + 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 @@ -11190,8 +14964,8 @@ namespace simdjson { #include namespace simdjson { - namespace arm64 { - namespace numberparsing { +namespace arm64 { +namespace numberparsing { #ifdef JSON_TEST_NUMBERS #define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) @@ -11207,540 +14981,547 @@ namespace simdjson { #define BIGINT_NUMBER(SRC) (BIGINT_ERROR) #endif - namespace { +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(negative)) << 63); - std::memcpy(&d, &mantissa, sizeof(d)); - return d; - } +// 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(negative)) << 63); + std::memcpy(&d, &mantissa, sizeof(d)); + return d; +} - // 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 +// 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) + if (0 <= power && power <= 22 && i <= 9007199254740991) #else - if (-22 <= power && power <= 22 && i <= 9007199254740991) + 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 + { + // 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. + // 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; - } + // 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 - // approximatively 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; + // 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 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. - simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]); - // 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. + // 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 - // 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. - simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); - 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); + // 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. - // 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 - } - } + // 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); - mantissa += mantissa & 1; - mantissa >>= 1; + // 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 + } + } - // 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; - } + mantissa += mantissa & 1; + mantissa >>= 1; - // 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(ptr)); - // We do not accept infinite values. + // 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; +} - // 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::max)() || *outDouble < std::numeric_limits::lowest()); - } +// 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(ptr)); + // We do not accept infinite values. - static bool parse_float_fallback(const uint8_t* ptr, const uint8_t* end_ptr, double* outDouble) { - *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(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::max)() || *outDouble < std::numeric_limits::lowest()); +} - // 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::max)() || *outDouble < std::numeric_limits::lowest()); - } +static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) { + *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(end_ptr)); + // We do not accept infinite values. - // 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); - } + // 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::max)() || *outDouble < std::numeric_limits::lowest()); +} - template - 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(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; - } +// 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); +} - simdjson_inline bool is_digit(const uint8_t c) { - return static_cast(c - '0') <= 9; - } +template +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(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 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; +simdjson_inline bool is_digit(const uint8_t c) { + return static_cast(c - '0') <= 9; +} + +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; #ifdef SIMDJSON_SWAR_NUMBER_PARSING #if SIMDJSON_SWAR_NUMBER_PARSING - // this helps if we have lots of decimals! - // this turns out to be frequent enough. - if (is_made_of_eight_digits_fast(p)) { - i = i * 100000000 + parse_eight_digits_unrolled(p); - p += 8; - } + // this helps if we have lots of decimals! + // this turns out to be frequent enough. + if (is_made_of_eight_digits_fast(p)) { + i = i * 100000000 + parse_eight_digits_unrolled(p); + p += 8; + } #endif // SIMDJSON_SWAR_NUMBER_PARSING #endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING - // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) - if (parse_digit(*p, i)) { ++p; } - while (parse_digit(*p, i)) { p++; } - exponent = first_after_period - p; - // Decimal without digits (123.) is illegal - if (exponent == 0) { - return INVALID_NUMBER(src); - } - return SUCCESS; - } + // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) + if (parse_digit(*p, i)) { ++p; } + while (parse_digit(*p, i)) { p++; } + exponent = first_after_period - p; + // Decimal without digits (123.) is illegal + if (exponent == 0) { + return INVALID_NUMBER(src); + } + return SUCCESS; +} - 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 +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. + // 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. + // 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 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; - } + // 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 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); - } +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 +} // unnamed namespace - /** @private */ - static 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 */ +static 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 - 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; - } +/** @private */ +template +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 - simdjson_inline error_code parse_number(const uint8_t* const src, W& writer); +// 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 +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 +// for performance analysis, it is sometimes useful to skip parsing #ifdef SIMDJSON_SKIPNUMBERPARSING - template - simdjson_inline error_code parse_number(const uint8_t* const, W& writer) { - writer.append_s64(0); // always write zero - return SUCCESS; // always succeeds - } +template +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 parse_unsigned(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline bool is_negative(const uint8_t* src) noexcept { return false; } - simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t* src) noexcept { return false; } - simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t* src) noexcept { return number_type::signed_integer; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t * src) noexcept { return number_type::signed_integer; } #else // parse the number at src @@ -11752,744 +15533,753 @@ namespace simdjson { // content and append a space before calling this function. // // Our objective is accurate parsing (ULP of 0) at high speed. - template - 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)) { 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 INVALID_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 { - WRITE_INTEGER(negative ? (~i + 1) : i, src, writer); - } - 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 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; - 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); - // 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 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 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; - 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); - // 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 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; - 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); - // 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 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 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; - while (parse_digit(*src, i)) { src++; } - - // 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 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; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (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 == '.')) { - p++; - const uint8_t* start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (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 = 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; - } - - simdjson_unused simdjson_inline bool is_negative(const uint8_t* src) noexcept { - return (*src == '-'); - } - - simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t* src) noexcept { - bool negative = (*src == '-'); - src += uint8_t(negative); - const uint8_t* p = src; - while (static_cast(*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 get_number_type(const uint8_t* src) noexcept { - bool negative = (*src == '-'); - src += uint8_t(negative); - const uint8_t* p = src; - while (static_cast(*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("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; - } - 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("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 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 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; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (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 == '.')) { - p++; - const uint8_t* start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (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 = 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; - } - - } // unnamed namespace +template +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)) { 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 INVALID_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 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; + 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); + // 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 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 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; + 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); + // 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 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; + 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); + // 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 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 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; + while (parse_digit(*src, i)) { src++; } + + // 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { + return (*src == '-'); +} + +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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 get_number_type(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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("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("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 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +} // unnamed namespace #endif // SIMDJSON_SKIPNUMBERPARSING - } // namespace numberparsing +} // 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; - } +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 arm64 } // namespace simdjson #endif // SIMDJSON_GENERIC_NUMBERPARSING_H @@ -12506,84 +16296,113 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { +namespace arm64 { - // - // internal::implementation_simdjson_result_base inline implementation - // +// +// internal::implementation_simdjson_result_base inline implementation +// - template - simdjson_inline void implementation_simdjson_result_base::tie(T& value, error_code& error) && noexcept { - error = this->second; - if (!error) { - value = std::forward>(*this).first; - } - } +template +simdjson_inline void implementation_simdjson_result_base::tie(T &value, error_code &error) && noexcept { + error = this->second; + if (!error) { + value = std::forward>(*this).first; + } +} - template - simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T& value) && noexcept { - error_code error; - std::forward>(*this).tie(value, error); - return error; - } +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T &value) && noexcept { + error_code error; + std::forward>(*this).tie(value, error); + return error; +} - template - simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { - return this->second; - } +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { + return this->second; +} + + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} #if SIMDJSON_EXCEPTIONS - template - simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return this->first; - } +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} - template - simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { - return std::forward>(*this).take_value(); - } +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} - template - simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return std::forward(this->first); - } +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} - template - simdjson_inline implementation_simdjson_result_base::operator T && () && noexcept(false) { - return std::forward>(*this).take_value(); - } + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + +template +simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return this->first; +} + +template +simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { + return std::forward>(*this).take_value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return std::forward(this->first); +} + +template +simdjson_inline implementation_simdjson_result_base::operator T&&() && noexcept(false) { + return std::forward>(*this).take_value(); +} #endif // SIMDJSON_EXCEPTIONS - template - simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { - return this->first; - } +template +simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { + return this->first; +} - template - simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { - return this->first; - } +template +simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { + return this->first; +} - template - simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { - return std::forward(this->first); - } +template +simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { + return std::forward(this->first); +} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T&& value, error_code error) noexcept - : first{ std::forward(value) }, second{ error } {} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept - : implementation_simdjson_result_base(T{}, error) {} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T&& value) noexcept - : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value, error_code error) noexcept + : first{std::forward(value)}, second{error} {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept + : implementation_simdjson_result_base(T{}, error) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value) noexcept + : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} - } // namespace arm64 +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H @@ -12622,14 +16441,14 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - /** - * Fallback implementation (runs on any machine). - */ - namespace fallback { +/** + * Fallback implementation (runs on any machine). + */ +namespace fallback { - class implementation; +class implementation; - } // namespace fallback +} // namespace fallback } // namespace simdjson #endif // SIMDJSON_FALLBACK_BASE_H @@ -12644,43 +16463,43 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace fallback { - namespace { +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; - } +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) { +/* 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; + 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); + return __builtin_clzll(input_num); #endif// _MSC_VER - } +} - } // unnamed namespace - } // namespace fallback +} // unnamed namespace +} // namespace fallback } // namespace simdjson #endif // SIMDJSON_FALLBACK_BITMANIPULATION_H @@ -12695,31 +16514,49 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace fallback { - namespace { +namespace fallback { +namespace { - // Holds backslashes and quotes locations. - struct backslash_and_quote { - public: - static constexpr uint32_t BYTES_PROCESSED = 1; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t* src, uint8_t* dst); +// 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; } + 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 + 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] }; - } +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] }; +} - } // unnamed namespace - } // namespace fallback + +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 @@ -12737,73 +16574,79 @@ namespace simdjson { #include #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); +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 { +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); - } +// 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(chars)); - } +/** @private */ +static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) { + return parse_eight_digits_unrolled(reinterpret_cast(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; - } +// 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; +/** @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; + // 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 + 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); + __uint128_t r = (static_cast<__uint128_t>(value1)) * value2; + answer.low = uint64_t(r); + answer.high = uint64_t(r >> 64); #endif - return answer; - } + return answer; +} - } // namespace numberparsing - } // namespace fallback +} // 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 */ @@ -12835,10 +16678,12 @@ namespace simdjson { /* 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_LSX */ -/* amalgamation skipped (editor-only): #include "simdjson/lsx/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 */ @@ -12848,22 +16693,22 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace fallback { +namespace fallback { - struct open_container; - class dom_parser_implementation; +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 - }; +/** + * 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 fallback } // namespace simdjson #endif // SIMDJSON_GENERIC_BASE_H @@ -12880,99 +16725,97 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace fallback { - namespace { - namespace jsoncharutils { +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]; - } +// 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]; - } +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; - } +// 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 - } +// 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; - } +// 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 jsoncharutils +} // unnamed namespace +} // namespace fallback } // namespace simdjson #endif // SIMDJSON_GENERIC_JSONCHARUTILS_H @@ -12990,69 +16833,69 @@ namespace simdjson { #include namespace simdjson { - namespace fallback { - namespace { - /// @private - namespace atomparsing { +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; } +// 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; } - // 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); - } +// 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); +} - 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) { + 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_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) { + 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_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) { + 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; } - } +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; } +} - } // namespace atomparsing - } // unnamed namespace - } // namespace fallback +} // namespace atomparsing +} // unnamed namespace +} // namespace fallback } // namespace simdjson #endif // SIMDJSON_GENERIC_ATOMPARSING_H @@ -13068,83 +16911,83 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace fallback { +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 +// 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"); +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_containers{}; - /** Whether each open container is a [ or { */ - std::unique_ptr 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{}; +class dom_parser_implementation final : public internal::dom_parser_implementation { +public: + /** Tape location of each open { or [ */ + std::unique_ptr open_containers{}; + /** Whether each open container is a [ or { */ + std::unique_ptr 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; + 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); + 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 fallback } // namespace simdjson namespace simdjson { - namespace fallback { +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; +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) { return CAPACITY; } - // Stage 1 index output - size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; - 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; +// 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) { return CAPACITY; } + // Stage 1 index output + size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; + 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; - } + _capacity = capacity; + return SUCCESS; +} - inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { - // 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; } +inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { + // 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; - } + _max_depth = max_depth; + return SUCCESS; +} - } // namespace fallback +} // namespace fallback } // namespace simdjson #endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H @@ -13159,129 +17002,148 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace fallback { +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 : public internal::implementation_simdjson_result_base { - * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} - * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} - * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} - * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} - * // Your extra methods here - * } - * - * Then any method returning simdjson_result will be chainable with your methods. - */ - template - struct implementation_simdjson_result_base { +// 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 : public internal::implementation_simdjson_result_base { + * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} + * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} + * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} + * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} + * // Your extra methods here + * } + * + * Then any method returning simdjson_result will be chainable with your methods. + */ +template +struct implementation_simdjson_result_base { - /** - * Create a new empty result with error = UNINITIALIZED. - */ - simdjson_inline implementation_simdjson_result_base() noexcept = default; + /** + * 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 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 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; + /** + * 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 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; + /** + * 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_inline error_code error() const 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& value() & noexcept(false); + /** + * 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); - /** - * Take the result value (move it). - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline T&& value() && 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&& take_value() && noexcept(false); + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& 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); + /** + * 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; - 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 + /** + * 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; - } // namespace fallback + 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 @@ -13302,8 +17164,8 @@ namespace simdjson { #include namespace simdjson { - namespace fallback { - namespace numberparsing { +namespace fallback { +namespace numberparsing { #ifdef JSON_TEST_NUMBERS #define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) @@ -13319,540 +17181,547 @@ namespace simdjson { #define BIGINT_NUMBER(SRC) (BIGINT_ERROR) #endif - namespace { +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(negative)) << 63); - std::memcpy(&d, &mantissa, sizeof(d)); - return d; - } +// 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(negative)) << 63); + std::memcpy(&d, &mantissa, sizeof(d)); + return d; +} - // 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 +// 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) + if (0 <= power && power <= 22 && i <= 9007199254740991) #else - if (-22 <= power && power <= 22 && i <= 9007199254740991) + 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 + { + // 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. + // 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; - } + // 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 - // approximatively 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; + // 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 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. - simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]); - // 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. + // 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 - // 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. - simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); - 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); + // 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. - // 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 - } - } + // 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); - mantissa += mantissa & 1; - mantissa >>= 1; + // 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 + } + } - // 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; - } + mantissa += mantissa & 1; + mantissa >>= 1; - // 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(ptr)); - // We do not accept infinite values. + // 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; +} - // 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::max)() || *outDouble < std::numeric_limits::lowest()); - } +// 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(ptr)); + // We do not accept infinite values. - static bool parse_float_fallback(const uint8_t* ptr, const uint8_t* end_ptr, double* outDouble) { - *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(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::max)() || *outDouble < std::numeric_limits::lowest()); +} - // 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::max)() || *outDouble < std::numeric_limits::lowest()); - } +static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) { + *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(end_ptr)); + // We do not accept infinite values. - // 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); - } + // 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::max)() || *outDouble < std::numeric_limits::lowest()); +} - template - 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(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; - } +// 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); +} - simdjson_inline bool is_digit(const uint8_t c) { - return static_cast(c - '0') <= 9; - } +template +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(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 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; +simdjson_inline bool is_digit(const uint8_t c) { + return static_cast(c - '0') <= 9; +} + +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; #ifdef SIMDJSON_SWAR_NUMBER_PARSING #if SIMDJSON_SWAR_NUMBER_PARSING - // this helps if we have lots of decimals! - // this turns out to be frequent enough. - if (is_made_of_eight_digits_fast(p)) { - i = i * 100000000 + parse_eight_digits_unrolled(p); - p += 8; - } + // this helps if we have lots of decimals! + // this turns out to be frequent enough. + if (is_made_of_eight_digits_fast(p)) { + i = i * 100000000 + parse_eight_digits_unrolled(p); + p += 8; + } #endif // SIMDJSON_SWAR_NUMBER_PARSING #endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING - // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) - if (parse_digit(*p, i)) { ++p; } - while (parse_digit(*p, i)) { p++; } - exponent = first_after_period - p; - // Decimal without digits (123.) is illegal - if (exponent == 0) { - return INVALID_NUMBER(src); - } - return SUCCESS; - } + // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) + if (parse_digit(*p, i)) { ++p; } + while (parse_digit(*p, i)) { p++; } + exponent = first_after_period - p; + // Decimal without digits (123.) is illegal + if (exponent == 0) { + return INVALID_NUMBER(src); + } + return SUCCESS; +} - 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 +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. + // 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. + // 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 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; - } + // 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 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); - } +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 +} // unnamed namespace - /** @private */ - static 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 */ +static 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 - 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; - } +/** @private */ +template +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 - simdjson_inline error_code parse_number(const uint8_t* const src, W& writer); +// 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 +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 +// for performance analysis, it is sometimes useful to skip parsing #ifdef SIMDJSON_SKIPNUMBERPARSING - template - simdjson_inline error_code parse_number(const uint8_t* const, W& writer) { - writer.append_s64(0); // always write zero - return SUCCESS; // always succeeds - } +template +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 parse_unsigned(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline bool is_negative(const uint8_t* src) noexcept { return false; } - simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t* src) noexcept { return false; } - simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t* src) noexcept { return number_type::signed_integer; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t * src) noexcept { return number_type::signed_integer; } #else // parse the number at src @@ -13864,744 +17733,753 @@ namespace simdjson { // content and append a space before calling this function. // // Our objective is accurate parsing (ULP of 0) at high speed. - template - 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)) { 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 INVALID_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 { - WRITE_INTEGER(negative ? (~i + 1) : i, src, writer); - } - 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 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; - 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); - // 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 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 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; - 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); - // 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 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; - 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); - // 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 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 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; - while (parse_digit(*src, i)) { src++; } - - // 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 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; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (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 == '.')) { - p++; - const uint8_t* start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (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 = 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; - } - - simdjson_unused simdjson_inline bool is_negative(const uint8_t* src) noexcept { - return (*src == '-'); - } - - simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t* src) noexcept { - bool negative = (*src == '-'); - src += uint8_t(negative); - const uint8_t* p = src; - while (static_cast(*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 get_number_type(const uint8_t* src) noexcept { - bool negative = (*src == '-'); - src += uint8_t(negative); - const uint8_t* p = src; - while (static_cast(*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("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; - } - 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("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 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 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; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (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 == '.')) { - p++; - const uint8_t* start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (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 = 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; - } - - } // unnamed namespace +template +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)) { 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 INVALID_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 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; + 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); + // 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 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 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; + 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); + // 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 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; + 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); + // 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 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 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; + while (parse_digit(*src, i)) { src++; } + + // 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { + return (*src == '-'); +} + +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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 get_number_type(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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("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("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 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +} // unnamed namespace #endif // SIMDJSON_SKIPNUMBERPARSING - } // namespace numberparsing +} // 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; - } +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 fallback } // namespace simdjson #endif // SIMDJSON_GENERIC_NUMBERPARSING_H @@ -14618,84 +18496,113 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace fallback { +namespace fallback { - // - // internal::implementation_simdjson_result_base inline implementation - // +// +// internal::implementation_simdjson_result_base inline implementation +// - template - simdjson_inline void implementation_simdjson_result_base::tie(T& value, error_code& error) && noexcept { - error = this->second; - if (!error) { - value = std::forward>(*this).first; - } - } +template +simdjson_inline void implementation_simdjson_result_base::tie(T &value, error_code &error) && noexcept { + error = this->second; + if (!error) { + value = std::forward>(*this).first; + } +} - template - simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T& value) && noexcept { - error_code error; - std::forward>(*this).tie(value, error); - return error; - } +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T &value) && noexcept { + error_code error; + std::forward>(*this).tie(value, error); + return error; +} - template - simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { - return this->second; - } +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { + return this->second; +} + + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} #if SIMDJSON_EXCEPTIONS - template - simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return this->first; - } +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} - template - simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { - return std::forward>(*this).take_value(); - } +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} - template - simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return std::forward(this->first); - } +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} - template - simdjson_inline implementation_simdjson_result_base::operator T && () && noexcept(false) { - return std::forward>(*this).take_value(); - } + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + +template +simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return this->first; +} + +template +simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { + return std::forward>(*this).take_value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return std::forward(this->first); +} + +template +simdjson_inline implementation_simdjson_result_base::operator T&&() && noexcept(false) { + return std::forward>(*this).take_value(); +} #endif // SIMDJSON_EXCEPTIONS - template - simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { - return this->first; - } +template +simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { + return this->first; +} - template - simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { - return this->first; - } +template +simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { + return this->first; +} - template - simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { - return std::forward(this->first); - } +template +simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { + return std::forward(this->first); +} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T&& value, error_code error) noexcept - : first{ std::forward(value) }, second{ error } {} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept - : implementation_simdjson_result_base(T{}, error) {} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T&& value) noexcept - : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value, error_code error) noexcept + : first{std::forward(value)}, second{error} {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept + : implementation_simdjson_result_base(T{}, error) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value) noexcept + : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} - } // namespace fallback +} // namespace fallback } // namespace simdjson #endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H @@ -14735,21 +18642,21 @@ namespace simdjson { // 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 { +/** + * Implementation for Haswell (Intel AVX2). + */ +namespace haswell { - class implementation; +class implementation; - namespace { - namespace simd { - template struct simd8; - template struct simd8x64; - } // namespace simd - } // unnamed namespace +namespace { +namespace simd { +template struct simd8; +template struct simd8x64; +} // namespace simd +} // unnamed namespace - } // namespace haswell +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_HASWELL_BASE_H @@ -14797,8 +18704,8 @@ namespace simdjson { #include #include #include // for _mm_clmulepi64_si128 - // unfortunately, we may not get _blsr_u64, but, thankfully, clang - // has it as a macro. +// 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) @@ -14811,8 +18718,14 @@ static_assert(sizeof(__m256i) <= simdjson::SIMDJSON_PADDING, "insufficient paddi /* 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 */ @@ -14826,64 +18739,64 @@ SIMDJSON_TARGET_REGION("avx2,bmi,pclmul,lzcnt,popcnt") /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace { +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) { +// 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); + 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); + //////// + // 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 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)); - } +/* 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 - } +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); - } +simdjson_inline long long int count_ones(uint64_t input_num) { + return _popcnt64(input_num); +} #endif - simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, - uint64_t* result) { +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { #if SIMDJSON_REGULAR_VISUAL_STUDIO - return _addcarry_u64(0, value1, value2, - reinterpret_cast(result)); + return _addcarry_u64(0, value1, value2, + reinterpret_cast(result)); #else - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); #endif - } +} - } // unnamed namespace - } // namespace haswell +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_HASWELL_BITMANIPULATION_H @@ -14899,24 +18812,24 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace { +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); - } +// +// 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 +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_HASWELL_BITMASK_H @@ -14936,49 +18849,49 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace numberparsing { +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(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 */ +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(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; +/** @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; + // 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 + 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); + __uint128_t r = (static_cast<__uint128_t>(value1)) * value2; + answer.low = uint64_t(r); + answer.high = uint64_t(r >> 64); #endif - return answer; - } + return answer; +} - } // namespace numberparsing - } // namespace haswell +} // namespace numberparsing +} // namespace haswell } // namespace simdjson #define SIMDJSON_SWAR_NUMBER_PARSING 1 @@ -14998,364 +18911,365 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace { - namespace simd { +namespace haswell { +namespace { +namespace simd { - // Forward-declared so they can be used by splat and friends. - template - struct base { - __m256i value; + // Forward-declared so they can be used by splat and friends. + template + struct base { + __m256i value; - // Zero constructor - simdjson_inline base() : value{ __m256i() } {} + // Zero constructor + simdjson_inline base() : value{__m256i()} {} - // Conversion from SIMD register - simdjson_inline base(const __m256i _value) : value(_value) {} + // 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; } + // 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(this); *this_cast = *this_cast | other; return *this_cast; } - simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } - simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } - }; + // 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(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; - // Forward-declared so they can be used by splat and friends. - template - struct simd8; + // Forward-declared so they can be used by splat and friends. + template + struct simd8; - template> - struct base8 : base> { - typedef uint32_t bitmask_t; - typedef uint64_t bitmask2_t; + template> + struct base8: base> { + typedef uint32_t bitmask_t; + typedef uint64_t bitmask2_t; - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m256i _value) : base>(_value) {} + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m256i _value) : base>(_value) {} - friend simdjson_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm256_cmpeq_epi8(lhs, rhs); } + friend simdjson_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm256_cmpeq_epi8(lhs, rhs); } - static const int SIZE = sizeof(base::value); + static const int SIZE = sizeof(base::value); - template - simdjson_inline simd8 prev(const simd8 prev_chunk) const { - return _mm256_alignr_epi8(*this, _mm256_permute2x128_si256(prev_chunk, *this, 0x21), 16 - N); - } - }; + template + simdjson_inline simd8 prev(const simd8 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 : base8 { - static simdjson_inline simd8 splat(bool _value) { return _mm256_set1_epi8(uint8_t(-(!!_value))); } + // SIMD byte mask type (returned by things like eq and gt) + template<> + struct simd8: base8 { + static simdjson_inline simd8 splat(bool _value) { return _mm256_set1_epi8(uint8_t(-(!!_value))); } - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m256i _value) : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) : base8(splat(_value)) {} + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m256i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(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 operator~() const { return *this ^ true; } - }; + 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 operator~() const { return *this ^ true; } + }; - template - struct base8_numeric : base8 { - static simdjson_inline simd8 splat(T _value) { return _mm256_set1_epi8(_value); } - static simdjson_inline simd8 zero() { return _mm256_setzero_si256(); } - static simdjson_inline simd8 load(const T values[32]) { - return _mm256_loadu_si256(reinterpret_cast(values)); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdjson_inline simd8 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( - 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 - ); - } + template + struct base8_numeric: base8 { + static simdjson_inline simd8 splat(T _value) { return _mm256_set1_epi8(_value); } + static simdjson_inline simd8 zero() { return _mm256_setzero_si256(); } + static simdjson_inline simd8 load(const T values[32]) { + return _mm256_loadu_si256(reinterpret_cast(values)); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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( + 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() {} - simdjson_inline base8_numeric(const __m256i _value) : base8(_value) {} + simdjson_inline base8_numeric() : base8() {} + simdjson_inline base8_numeric(const __m256i _value) : base8(_value) {} - // Store to array - simdjson_inline void store(T dst[32]) const { return _mm256_storeu_si256(reinterpret_cast<__m256i*>(dst), *this); } + // 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 operator+(const simd8 other) const { return _mm256_add_epi8(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return _mm256_sub_epi8(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { return _mm256_add_epi8(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return _mm256_sub_epi8(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } - // Override to distinguish from bool version - simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return _mm256_shuffle_epi8(lookup_table, *this); - } + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 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 compress(uint32_t mask) would be - // sensible, but the AVX ISA makes this kind of approach difficult. - template - 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]; + // 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 compress(uint32_t mask) would be + // sensible, but the AVX ISA makes this kind of approach difficult. + template + 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(pshufb_combine_table + pop1 * 8))); - __m256i compactmask = _mm256_insertf128_si256(v256, - _mm_loadu_si128(reinterpret_cast(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); - } + // 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(pshufb_combine_table + pop1 * 8))); + __m256i compactmask = _mm256_insertf128_si256(v256, + _mm_loadu_si128(reinterpret_cast(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 - simdjson_inline simd8 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::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - }; + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; - // Signed bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( - 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 - ); - } + // Signed bytes + template<> + struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( + 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 max_val(const simd8 other) const { return _mm256_max_epi8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm256_min_epi8(*this, other); } - simdjson_inline simd8 operator>(const simd8 other) const { return _mm256_cmpgt_epi8(*this, other); } - simdjson_inline simd8 operator<(const simd8 other) const { return _mm256_cmpgt_epi8(other, *this); } - }; + // Order-sensitive comparisons + simdjson_inline simd8 max_val(const simd8 other) const { return _mm256_max_epi8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm256_min_epi8(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return _mm256_cmpgt_epi8(*this, other); } + simdjson_inline simd8 operator<(const simd8 other) const { return _mm256_cmpgt_epi8(other, *this); } + }; - // Unsigned bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( - 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 - ); - } + // Unsigned bytes + template<> + struct simd8: base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( + 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 saturating_add(const simd8 other) const { return _mm256_adds_epu8(*this, other); } - simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm256_subs_epu8(*this, other); } + // Saturated math + simdjson_inline simd8 saturating_add(const simd8 other) const { return _mm256_adds_epu8(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm256_subs_epu8(*this, other); } - // Order-specific operations - simdjson_inline simd8 max_val(const simd8 other) const { return _mm256_max_epu8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm256_min_epu8(other, *this); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } - simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } - simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } - simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } + // Order-specific operations + simdjson_inline simd8 max_val(const simd8 other) const { return _mm256_max_epu8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm256_min_epu8(other, *this); } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } + simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } + simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } + simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } - // Bit-specific operations - simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } - simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } - simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } - simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return _mm256_testz_si256(*this, bits); } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } - template - simdjson_inline simd8 shr() const { return simd8(_mm256_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } - template - simdjson_inline simd8 shl() const { return simd8(_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 - simdjson_inline int get_bit() const { return _mm256_movemask_epi8(_mm256_slli_epi16(*this, 7 - N)); } - }; + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } + simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } + simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } + simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return _mm256_testz_si256(*this, bits); } + simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } + template + simdjson_inline simd8 shr() const { return simd8(_mm256_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } + template + simdjson_inline simd8 shl() const { return simd8(_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 + simdjson_inline int get_bit() const { return _mm256_movemask_epi8(_mm256_slli_epi16(*this, 7-N)); } + }; - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 2, "Haswell kernel should use two registers per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 2, "Haswell kernel should use two registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1) : chunks{ chunk0, chunk1 } {} - simdjson_inline simd8x64(const T ptr[64]) : chunks{ simd8::load(ptr), simd8::load(ptr + 32) } {} + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1) : chunks{chunk0, chunk1} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::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 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) * 0); - this->chunks[1].store(ptr + sizeof(simd8) * 1); - } + simdjson_inline void store(T ptr[64]) const { + this->chunks[0].store(ptr+sizeof(simd8)*0); + this->chunks[1].store(ptr+sizeof(simd8)*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 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 reduce_or() const { - return this->chunks[0] | this->chunks[1]; - } + simdjson_inline simd8 reduce_or() const { + return this->chunks[0] | this->chunks[1]; + } - simdjson_inline simd8x64 bit_or(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] | mask, - this->chunks[1] | mask - ); - } + simdjson_inline simd8x64 bit_or(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] | mask, + this->chunks[1] | mask + ); + } - simdjson_inline uint64_t eq(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] == mask, - this->chunks[1] == mask - ).to_bitmask(); - } + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] == mask, + this->chunks[1] == mask + ).to_bitmask(); + } - simdjson_inline uint64_t eq(const simd8x64& other) const { - return simd8x64( - this->chunks[0] == other.chunks[0], - this->chunks[1] == other.chunks[1] - ).to_bitmask(); - } + simdjson_inline uint64_t eq(const simd8x64 &other) const { + return simd8x64( + 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] <= mask, - this->chunks[1] <= mask - ).to_bitmask(); - } - }; // struct simd8x64 + simdjson_inline uint64_t lteq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] <= mask, + this->chunks[1] <= mask + ).to_bitmask(); + } + }; // struct simd8x64 - } // namespace simd +} // namespace simd - } // unnamed namespace - } // namespace haswell +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_HASWELL_SIMD_H @@ -15372,41 +19286,66 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace { +namespace haswell { +namespace { - using namespace simd; +using namespace simd; - // Holds backslashes and quotes locations. - struct backslash_and_quote { - public: - static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t* src, uint8_t* dst); +// 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); } + 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 + 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 v(src); - // store to dest unconditionally - we can overwrite the bits we don't like later - v.store(dst); - return { - static_cast((v == '\\').to_bitmask()), // bs_bits - static_cast((v == '"').to_bitmask()), // quote_bits - }; - } +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 v(src); + // store to dest unconditionally - we can overwrite the bits we don't like later + v.store(dst); + return { + static_cast((v == '\\').to_bitmask()), // bs_bits + static_cast((v == '"').to_bitmask()), // quote_bits + }; +} - } // unnamed namespace - } // namespace haswell + +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 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 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 @@ -15439,10 +19378,12 @@ namespace simdjson { /* 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_LSX */ -/* amalgamation skipped (editor-only): #include "simdjson/lsx/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 */ @@ -15452,22 +19393,22 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { +namespace haswell { - struct open_container; - class dom_parser_implementation; +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 - }; +/** + * 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 haswell } // namespace simdjson #endif // SIMDJSON_GENERIC_BASE_H @@ -15484,99 +19425,97 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace { - namespace jsoncharutils { +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]; - } +// 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]; - } +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; - } +// 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 - } +// 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; - } +// 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 jsoncharutils +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_GENERIC_JSONCHARUTILS_H @@ -15594,69 +19533,69 @@ namespace simdjson { #include namespace simdjson { - namespace haswell { - namespace { - /// @private - namespace atomparsing { +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; } +// 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; } - // 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); - } +// 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); +} - 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) { + 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_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) { + 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_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) { + 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; } - } +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; } +} - } // namespace atomparsing - } // unnamed namespace - } // namespace haswell +} // namespace atomparsing +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_GENERIC_ATOMPARSING_H @@ -15672,83 +19611,83 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { +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 +// 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"); +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_containers{}; - /** Whether each open container is a [ or { */ - std::unique_ptr 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{}; +class dom_parser_implementation final : public internal::dom_parser_implementation { +public: + /** Tape location of each open { or [ */ + std::unique_ptr open_containers{}; + /** Whether each open container is a [ or { */ + std::unique_ptr 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; + 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); + 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 haswell } // namespace simdjson namespace simdjson { - namespace haswell { +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; +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) { return CAPACITY; } - // Stage 1 index output - size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; - 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; +// 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) { return CAPACITY; } + // Stage 1 index output + size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; + 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; - } + _capacity = capacity; + return SUCCESS; +} - inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { - // 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; } +inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { + // 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; - } + _max_depth = max_depth; + return SUCCESS; +} - } // namespace haswell +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H @@ -15763,129 +19702,148 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { +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 : public internal::implementation_simdjson_result_base { - * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} - * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} - * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} - * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} - * // Your extra methods here - * } - * - * Then any method returning simdjson_result will be chainable with your methods. - */ - template - struct implementation_simdjson_result_base { +// 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 : public internal::implementation_simdjson_result_base { + * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} + * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} + * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} + * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} + * // Your extra methods here + * } + * + * Then any method returning simdjson_result will be chainable with your methods. + */ +template +struct implementation_simdjson_result_base { - /** - * Create a new empty result with error = UNINITIALIZED. - */ - simdjson_inline implementation_simdjson_result_base() noexcept = default; + /** + * 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 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 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; + /** + * 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 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; + /** + * 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_inline error_code error() const 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& value() & noexcept(false); + /** + * 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); - /** - * Take the result value (move it). - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline T&& value() && 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&& take_value() && noexcept(false); + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& 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); + /** + * 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; - 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 + /** + * 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; - } // namespace haswell + 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 @@ -15906,8 +19864,8 @@ namespace simdjson { #include namespace simdjson { - namespace haswell { - namespace numberparsing { +namespace haswell { +namespace numberparsing { #ifdef JSON_TEST_NUMBERS #define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) @@ -15923,540 +19881,547 @@ namespace simdjson { #define BIGINT_NUMBER(SRC) (BIGINT_ERROR) #endif - namespace { +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(negative)) << 63); - std::memcpy(&d, &mantissa, sizeof(d)); - return d; - } +// 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(negative)) << 63); + std::memcpy(&d, &mantissa, sizeof(d)); + return d; +} - // 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 +// 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) + if (0 <= power && power <= 22 && i <= 9007199254740991) #else - if (-22 <= power && power <= 22 && i <= 9007199254740991) + 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 + { + // 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. + // 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; - } + // 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 - // approximatively 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; + // 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 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. - simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]); - // 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. + // 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 - // 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. - simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); - 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); + // 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. - // 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 - } - } + // 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); - mantissa += mantissa & 1; - mantissa >>= 1; + // 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 + } + } - // 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; - } + mantissa += mantissa & 1; + mantissa >>= 1; - // 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(ptr)); - // We do not accept infinite values. + // 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; +} - // 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::max)() || *outDouble < std::numeric_limits::lowest()); - } +// 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(ptr)); + // We do not accept infinite values. - static bool parse_float_fallback(const uint8_t* ptr, const uint8_t* end_ptr, double* outDouble) { - *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(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::max)() || *outDouble < std::numeric_limits::lowest()); +} - // 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::max)() || *outDouble < std::numeric_limits::lowest()); - } +static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) { + *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(end_ptr)); + // We do not accept infinite values. - // 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); - } + // 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::max)() || *outDouble < std::numeric_limits::lowest()); +} - template - 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(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; - } +// 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); +} - simdjson_inline bool is_digit(const uint8_t c) { - return static_cast(c - '0') <= 9; - } +template +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(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 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; +simdjson_inline bool is_digit(const uint8_t c) { + return static_cast(c - '0') <= 9; +} + +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; #ifdef SIMDJSON_SWAR_NUMBER_PARSING #if SIMDJSON_SWAR_NUMBER_PARSING - // this helps if we have lots of decimals! - // this turns out to be frequent enough. - if (is_made_of_eight_digits_fast(p)) { - i = i * 100000000 + parse_eight_digits_unrolled(p); - p += 8; - } + // this helps if we have lots of decimals! + // this turns out to be frequent enough. + if (is_made_of_eight_digits_fast(p)) { + i = i * 100000000 + parse_eight_digits_unrolled(p); + p += 8; + } #endif // SIMDJSON_SWAR_NUMBER_PARSING #endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING - // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) - if (parse_digit(*p, i)) { ++p; } - while (parse_digit(*p, i)) { p++; } - exponent = first_after_period - p; - // Decimal without digits (123.) is illegal - if (exponent == 0) { - return INVALID_NUMBER(src); - } - return SUCCESS; - } + // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) + if (parse_digit(*p, i)) { ++p; } + while (parse_digit(*p, i)) { p++; } + exponent = first_after_period - p; + // Decimal without digits (123.) is illegal + if (exponent == 0) { + return INVALID_NUMBER(src); + } + return SUCCESS; +} - 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 +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. + // 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. + // 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 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; - } + // 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 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); - } +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 +} // unnamed namespace - /** @private */ - static 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 */ +static 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 - 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; - } +/** @private */ +template +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 - simdjson_inline error_code parse_number(const uint8_t* const src, W& writer); +// 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 +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 +// for performance analysis, it is sometimes useful to skip parsing #ifdef SIMDJSON_SKIPNUMBERPARSING - template - simdjson_inline error_code parse_number(const uint8_t* const, W& writer) { - writer.append_s64(0); // always write zero - return SUCCESS; // always succeeds - } +template +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 parse_unsigned(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline bool is_negative(const uint8_t* src) noexcept { return false; } - simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t* src) noexcept { return false; } - simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t* src) noexcept { return number_type::signed_integer; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t * src) noexcept { return number_type::signed_integer; } #else // parse the number at src @@ -16468,744 +20433,753 @@ namespace simdjson { // content and append a space before calling this function. // // Our objective is accurate parsing (ULP of 0) at high speed. - template - 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)) { 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 INVALID_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 { - WRITE_INTEGER(negative ? (~i + 1) : i, src, writer); - } - 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 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; - 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); - // 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 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 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; - 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); - // 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 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; - 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); - // 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 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 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; - while (parse_digit(*src, i)) { src++; } - - // 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 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; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (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 == '.')) { - p++; - const uint8_t* start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (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 = 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; - } - - simdjson_unused simdjson_inline bool is_negative(const uint8_t* src) noexcept { - return (*src == '-'); - } - - simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t* src) noexcept { - bool negative = (*src == '-'); - src += uint8_t(negative); - const uint8_t* p = src; - while (static_cast(*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 get_number_type(const uint8_t* src) noexcept { - bool negative = (*src == '-'); - src += uint8_t(negative); - const uint8_t* p = src; - while (static_cast(*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("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; - } - 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("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 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 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; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (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 == '.')) { - p++; - const uint8_t* start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (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 = 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; - } - - } // unnamed namespace +template +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)) { 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 INVALID_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 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; + 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); + // 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 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 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; + 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); + // 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 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; + 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); + // 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 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 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; + while (parse_digit(*src, i)) { src++; } + + // 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { + return (*src == '-'); +} + +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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 get_number_type(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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("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("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 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +} // unnamed namespace #endif // SIMDJSON_SKIPNUMBERPARSING - } // namespace numberparsing +} // 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; - } +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 haswell } // namespace simdjson #endif // SIMDJSON_GENERIC_NUMBERPARSING_H @@ -17222,84 +21196,113 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { +namespace haswell { - // - // internal::implementation_simdjson_result_base inline implementation - // +// +// internal::implementation_simdjson_result_base inline implementation +// - template - simdjson_inline void implementation_simdjson_result_base::tie(T& value, error_code& error) && noexcept { - error = this->second; - if (!error) { - value = std::forward>(*this).first; - } - } +template +simdjson_inline void implementation_simdjson_result_base::tie(T &value, error_code &error) && noexcept { + error = this->second; + if (!error) { + value = std::forward>(*this).first; + } +} - template - simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T& value) && noexcept { - error_code error; - std::forward>(*this).tie(value, error); - return error; - } +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T &value) && noexcept { + error_code error; + std::forward>(*this).tie(value, error); + return error; +} - template - simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { - return this->second; - } +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { + return this->second; +} + + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} #if SIMDJSON_EXCEPTIONS - template - simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return this->first; - } +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} - template - simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { - return std::forward>(*this).take_value(); - } +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} - template - simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return std::forward(this->first); - } +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} - template - simdjson_inline implementation_simdjson_result_base::operator T && () && noexcept(false) { - return std::forward>(*this).take_value(); - } + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + +template +simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return this->first; +} + +template +simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { + return std::forward>(*this).take_value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return std::forward(this->first); +} + +template +simdjson_inline implementation_simdjson_result_base::operator T&&() && noexcept(false) { + return std::forward>(*this).take_value(); +} #endif // SIMDJSON_EXCEPTIONS - template - simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { - return this->first; - } +template +simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { + return this->first; +} - template - simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { - return this->first; - } +template +simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { + return this->first; +} - template - simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { - return std::forward(this->first); - } +template +simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { + return std::forward(this->first); +} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T&& value, error_code error) noexcept - : first{ std::forward(value) }, second{ error } {} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept - : implementation_simdjson_result_base(T{}, error) {} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T&& value) noexcept - : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value, error_code error) noexcept + : first{std::forward(value)}, second{error} {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept + : implementation_simdjson_result_base(T{}, error) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value) noexcept + : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} - } // namespace haswell +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H @@ -17342,14 +21345,14 @@ SIMDJSON_UNTARGET_REGION // 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 { +/** + * Implementation for Icelake (Intel AVX512). + */ +namespace icelake { - class implementation; +class implementation; - } // namespace icelake +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_ICELAKE_BASE_H @@ -17397,7 +21400,7 @@ namespace simdjson { #include #include #include // for _mm_clmulepi64_si128 - // Important: we need the AVX-512 headers: +// Important: we need the AVX-512 headers: #include #include #include @@ -17433,64 +21436,64 @@ SIMDJSON_TARGET_REGION("avx512f,avx512dq,avx512cd,avx512bw,avx512vbmi,avx512vbmi /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { - namespace { +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) { +// 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); + 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); + //////// + // 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 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)); - } +/* 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 - } +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); - } +simdjson_inline long long int count_ones(uint64_t input_num) { + return _popcnt64(input_num); +} #endif - simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, - uint64_t* result) { +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { #if SIMDJSON_REGULAR_VISUAL_STUDIO - return _addcarry_u64(0, value1, value2, - reinterpret_cast(result)); + return _addcarry_u64(0, value1, value2, + reinterpret_cast(result)); #else - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); #endif - } +} - } // unnamed namespace - } // namespace icelake +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_ICELAKE_BITMANIPULATION_H @@ -17506,24 +21509,24 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { - namespace { +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); - } +// +// 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 +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_ICELAKE_BITMASK_H @@ -17551,354 +21554,358 @@ namespace simdjson { * 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)); + 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 { +namespace icelake { +namespace { +namespace simd { - // Forward-declared so they can be used by splat and friends. - template - struct base { - __m512i value; + // Forward-declared so they can be used by splat and friends. + template + struct base { + __m512i value; - // Zero constructor - simdjson_inline base() : value{ __m512i() } {} + // Zero constructor + simdjson_inline base() : value{__m512i()} {} - // Conversion from SIMD register - simdjson_inline base(const __m512i _value) : value(_value) {} + // 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; } + // 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(this); *this_cast = *this_cast | other; return *this_cast; } - simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } - simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } - }; + // 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(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; - // Forward-declared so they can be used by splat and friends. - template - struct simd8; + // Forward-declared so they can be used by splat and friends. + template + struct simd8; - template> - struct base8 : base> { - typedef uint32_t bitmask_t; - typedef uint64_t bitmask2_t; + template> + struct base8: base> { + typedef uint32_t bitmask_t; + typedef uint64_t bitmask2_t; - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m512i _value) : base>(_value) {} + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m512i _value) : base>(_value) {} - friend simdjson_really_inline uint64_t operator==(const simd8 lhs, const simd8 rhs) { - return _mm512_cmpeq_epi8_mask(lhs, rhs); - } + friend simdjson_really_inline uint64_t operator==(const simd8 lhs, const simd8 rhs) { + return _mm512_cmpeq_epi8_mask(lhs, rhs); + } + static const int SIZE = sizeof(base::value); - static const int SIZE = sizeof(base::value); + template + simdjson_inline simd8 prev(const simd8 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); + } + }; - template - simdjson_inline simd8 prev(const simd8 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: base8 { + static simdjson_inline simd8 splat(bool _value) { return _mm512_set1_epi8(uint8_t(-(!!_value))); } - // SIMD byte mask type (returned by things like eq and gt) - template<> - struct simd8 : base8 { - static simdjson_inline simd8 splat(bool _value) { return _mm512_set1_epi8(uint8_t(-(!!_value))); } + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m512i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(splat(_value)) {} + simdjson_inline bool any() const { return !!_mm512_test_epi8_mask (*this, *this); } + simdjson_inline simd8 operator~() const { return *this ^ true; } + }; - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m512i _value) : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) : base8(splat(_value)) {} - simdjson_inline bool any() const { return !!_mm512_test_epi8_mask(*this, *this); } - simdjson_inline simd8 operator~() const { return *this ^ true; } - }; + template + struct base8_numeric: base8 { + static simdjson_inline simd8 splat(T _value) { return _mm512_set1_epi8(_value); } + static simdjson_inline simd8 zero() { return _mm512_setzero_si512(); } + static simdjson_inline simd8 load(const T values[64]) { + return _mm512_loadu_si512(reinterpret_cast(values)); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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( + 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 + ); + } - template - struct base8_numeric : base8 { - static simdjson_inline simd8 splat(T _value) { return _mm512_set1_epi8(_value); } - static simdjson_inline simd8 zero() { return _mm512_setzero_si512(); } - static simdjson_inline simd8 load(const T values[64]) { - return _mm512_loadu_si512(reinterpret_cast(values)); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdjson_inline simd8 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( - 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() {} + simdjson_inline base8_numeric(const __m512i _value) : base8(_value) {} - simdjson_inline base8_numeric() : base8() {} - simdjson_inline base8_numeric(const __m512i _value) : base8(_value) {} + // Store to array + simdjson_inline void store(T dst[64]) const { return _mm512_storeu_si512(reinterpret_cast<__m512i *>(dst), *this); } - // 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 operator+(const simd8 other) const { return _mm512_add_epi8(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return _mm512_sub_epi8(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } - // Addition/subtraction are the same for signed and unsigned - simdjson_inline simd8 operator+(const simd8 other) const { return _mm512_add_epi8(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return _mm512_sub_epi8(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - // Override to distinguish from bool version - simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 lookup_table) const { + return _mm512_shuffle_epi8(lookup_table, *this); + } - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 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 compress(uint32_t mask) would be + // sensible, but the AVX ISA makes this kind of approach difficult. + template + 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 + } - // 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 compress(uint32_t mask) would be - // sensible, but the AVX ISA makes this kind of approach difficult. - template - simdjson_inline void compress(uint64_t mask, L* output) const { - _mm512_mask_compressstoreu_epi8(output, ~mask, *this); - } + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; - template - simdjson_inline simd8 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::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - }; + // Signed bytes + template<> + struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m512i _value) : base8_numeric(_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 + )) {} - // Signed bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m512i _value) : base8_numeric(_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 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( + 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 + ); + } - // Repeat 16 values as many times as necessary (usually for lookup tables) - simdjson_inline static simd8 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( - 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 max_val(const simd8 other) const { return _mm512_max_epi8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm512_min_epi8(*this, other); } - // Order-sensitive comparisons - simdjson_inline simd8 max_val(const simd8 other) const { return _mm512_max_epi8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm512_min_epi8(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return _mm512_maskz_abs_epi8(_mm512_cmpgt_epi8_mask(*this, other),_mm512_set1_epi8(uint8_t(0x80))); } + simdjson_inline simd8 operator<(const simd8 other) const { return _mm512_maskz_abs_epi8(_mm512_cmpgt_epi8_mask(other, *this),_mm512_set1_epi8(uint8_t(0x80))); } + }; - simdjson_inline simd8 operator>(const simd8 other) const { return _mm512_maskz_abs_epi8(_mm512_cmpgt_epi8_mask(*this, other), _mm512_set1_epi8(uint8_t(0x80))); } - simdjson_inline simd8 operator<(const simd8 other) const { return _mm512_maskz_abs_epi8(_mm512_cmpgt_epi8_mask(other, *this), _mm512_set1_epi8(uint8_t(0x80))); } - }; + // Unsigned bytes + template<> + struct simd8: base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m512i _value) : base8_numeric(_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 + )) {} - // Unsigned bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m512i _value) : base8_numeric(_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 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( + 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 + ); + } - // Repeat 16 values as many times as necessary (usually for lookup tables) - simdjson_inline static simd8 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( - 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 saturating_add(const simd8 other) const { return _mm512_adds_epu8(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm512_subs_epu8(*this, other); } - // Saturated math - simdjson_inline simd8 saturating_add(const simd8 other) const { return _mm512_adds_epu8(*this, other); } - simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm512_subs_epu8(*this, other); } + // Order-specific operations + simdjson_inline simd8 max_val(const simd8 other) const { return _mm512_max_epu8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm512_min_epu8(other, *this); } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } + simdjson_inline uint64_t operator<=(const simd8 other) const { return other.max_val(*this) == other; } + simdjson_inline uint64_t operator>=(const simd8 other) const { return other.min_val(*this) == other; } + simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } - // Order-specific operations - simdjson_inline simd8 max_val(const simd8 other) const { return _mm512_max_epu8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm512_min_epu8(other, *this); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } - simdjson_inline uint64_t operator<=(const simd8 other) const { return other.max_val(*this) == other; } - simdjson_inline uint64_t operator>=(const simd8 other) const { return other.min_val(*this) == other; } - simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { return _mm512_mask_blend_epi8(*this == uint8_t(0), _mm512_set1_epi8(0), _mm512_set1_epi8(-1)); } + simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } + simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } + simdjson_inline simd8 any_bits_set(simd8 bits) const { return ~this->bits_not_set(bits); } - // Bit-specific operations - simdjson_inline simd8 bits_not_set() const { return _mm512_mask_blend_epi8(*this == uint8_t(0), _mm512_set1_epi8(0), _mm512_set1_epi8(-1)); } - simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } - simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } - simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return !_mm512_test_epi8_mask(*this, bits); } + simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } + template + simdjson_inline simd8 shr() const { return simd8(_mm512_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } + template + simdjson_inline simd8 shl() const { return simd8(_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 + simdjson_inline uint64_t get_bit() const { return _mm512_movepi8_mask(_mm512_slli_epi16(*this, 7-N)); } + }; - 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 bits) const { return !_mm512_test_epi8_mask(*this, bits); } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } - template - simdjson_inline simd8 shr() const { return simd8(_mm512_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } - template - simdjson_inline simd8 shl() const { return simd8(_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 - simdjson_inline uint64_t get_bit() const { return _mm512_movepi8_mask(_mm512_slli_epi16(*this, 7 - N)); } - }; + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 1, "Icelake kernel should use one register per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 1, "Icelake kernel should use one register per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1) : chunks{chunk0, chunk1} {} + simdjson_inline simd8x64(const simd8 chunk0) : chunks{chunk0} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr)} {} - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1) : chunks{ chunk0, chunk1 } {} - simdjson_inline simd8x64(const simd8 chunk0) : chunks{ chunk0 } {} - simdjson_inline simd8x64(const T ptr[64]) : chunks{ simd8::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 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)*0); + } - simdjson_inline void store(T ptr[64]) const { - this->chunks[0].store(ptr + sizeof(simd8) * 0); - } + simdjson_inline simd8 reduce_or() const { + return this->chunks[0]; + } - simdjson_inline simd8 reduce_or() const { - return this->chunks[0]; - } + simdjson_inline simd8x64 bit_or(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] | mask + ); + } - simdjson_inline simd8x64 bit_or(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] | mask - ); - } + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return this->chunks[0] == mask; + } - simdjson_inline uint64_t eq(const T m) const { - const simd8 mask = simd8::splat(m); - return this->chunks[0] == mask; - } + simdjson_inline uint64_t eq(const simd8x64 &other) const { + return this->chunks[0] == other.chunks[0]; + } - simdjson_inline uint64_t eq(const simd8x64& other) const { - return this->chunks[0] == other.chunks[0]; - } + simdjson_inline uint64_t lteq(const T m) const { + const simd8 mask = simd8::splat(m); + return this->chunks[0] <= mask; + } + }; // struct simd8x64 - simdjson_inline uint64_t lteq(const T m) const { - const simd8 mask = simd8::splat(m); - return this->chunks[0] <= mask; - } - }; // struct simd8x64 +} // namespace simd - } // namespace simd - - } // unnamed namespace - } // namespace icelake +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_ICELAKE_SIMD_H @@ -17915,41 +21922,70 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { - namespace { +namespace icelake { +namespace { - using namespace simd; +using namespace simd; - // Holds backslashes and quotes locations. - struct backslash_and_quote { - public: - static constexpr uint32_t BYTES_PROCESSED = 64; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t* src, uint8_t* dst); +// 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); } + 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 + 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 v(src); - // store to dest unconditionally - we can overwrite the bits we don't like later - v.store(dst); - return { - static_cast(v == '\\'), // bs_bits - static_cast(v == '"'), // quote_bits - }; - } +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 v(src); + // store to dest unconditionally - we can overwrite the bits we don't like later + v.store(dst); + return { + static_cast(v == '\\'), // bs_bits + static_cast(v == '"'), // quote_bits + }; +} - } // unnamed namespace - } // namespace icelake + + +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 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 @@ -17966,48 +22002,48 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { - namespace numberparsing { +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(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 - } +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(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; +/** @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; + // 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 + 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); + __uint128_t r = (static_cast<__uint128_t>(value1)) * value2; + answer.low = uint64_t(r); + answer.high = uint64_t(r >> 64); #endif - return answer; - } + return answer; +} - } // namespace numberparsing - } // namespace icelake +} // namespace numberparsing +} // namespace icelake } // namespace simdjson #define SIMDJSON_SWAR_NUMBER_PARSING 1 @@ -18042,10 +22078,12 @@ namespace simdjson { /* 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_LSX */ -/* amalgamation skipped (editor-only): #include "simdjson/lsx/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 */ @@ -18055,22 +22093,22 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { +namespace icelake { - struct open_container; - class dom_parser_implementation; +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 - }; +/** + * 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 icelake } // namespace simdjson #endif // SIMDJSON_GENERIC_BASE_H @@ -18087,99 +22125,97 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { - namespace { - namespace jsoncharutils { +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]; - } +// 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]; - } +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; - } +// 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 - } +// 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; - } +// 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 jsoncharutils +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_GENERIC_JSONCHARUTILS_H @@ -18197,69 +22233,69 @@ namespace simdjson { #include namespace simdjson { - namespace icelake { - namespace { - /// @private - namespace atomparsing { +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; } +// 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; } - // 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); - } +// 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); +} - 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) { + 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_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) { + 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_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) { + 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; } - } +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; } +} - } // namespace atomparsing - } // unnamed namespace - } // namespace icelake +} // namespace atomparsing +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_GENERIC_ATOMPARSING_H @@ -18275,83 +22311,83 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { +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 +// 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"); +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_containers{}; - /** Whether each open container is a [ or { */ - std::unique_ptr 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{}; +class dom_parser_implementation final : public internal::dom_parser_implementation { +public: + /** Tape location of each open { or [ */ + std::unique_ptr open_containers{}; + /** Whether each open container is a [ or { */ + std::unique_ptr 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; + 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); + 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 icelake } // namespace simdjson namespace simdjson { - namespace icelake { +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; +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) { return CAPACITY; } - // Stage 1 index output - size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; - 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; +// 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) { return CAPACITY; } + // Stage 1 index output + size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; + 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; - } + _capacity = capacity; + return SUCCESS; +} - inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { - // 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; } +inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { + // 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; - } + _max_depth = max_depth; + return SUCCESS; +} - } // namespace icelake +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H @@ -18366,129 +22402,148 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { +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 : public internal::implementation_simdjson_result_base { - * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} - * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} - * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} - * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} - * // Your extra methods here - * } - * - * Then any method returning simdjson_result will be chainable with your methods. - */ - template - struct implementation_simdjson_result_base { +// 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 : public internal::implementation_simdjson_result_base { + * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} + * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} + * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} + * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} + * // Your extra methods here + * } + * + * Then any method returning simdjson_result will be chainable with your methods. + */ +template +struct implementation_simdjson_result_base { - /** - * Create a new empty result with error = UNINITIALIZED. - */ - simdjson_inline implementation_simdjson_result_base() noexcept = default; + /** + * 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 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 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; + /** + * 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 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; + /** + * 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_inline error_code error() const 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& value() & noexcept(false); + /** + * 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); - /** - * Take the result value (move it). - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline T&& value() && 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&& take_value() && noexcept(false); + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& 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); + /** + * 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; - 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 + /** + * 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; - } // namespace icelake + 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 @@ -18509,8 +22564,8 @@ namespace simdjson { #include namespace simdjson { - namespace icelake { - namespace numberparsing { +namespace icelake { +namespace numberparsing { #ifdef JSON_TEST_NUMBERS #define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) @@ -18526,540 +22581,547 @@ namespace simdjson { #define BIGINT_NUMBER(SRC) (BIGINT_ERROR) #endif - namespace { +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(negative)) << 63); - std::memcpy(&d, &mantissa, sizeof(d)); - return d; - } +// 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(negative)) << 63); + std::memcpy(&d, &mantissa, sizeof(d)); + return d; +} - // 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 +// 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) + if (0 <= power && power <= 22 && i <= 9007199254740991) #else - if (-22 <= power && power <= 22 && i <= 9007199254740991) + 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 + { + // 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. + // 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; - } + // 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 - // approximatively 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; + // 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 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. - simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]); - // 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. + // 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 - // 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. - simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); - 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); + // 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. - // 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 - } - } + // 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); - mantissa += mantissa & 1; - mantissa >>= 1; + // 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 + } + } - // 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; - } + mantissa += mantissa & 1; + mantissa >>= 1; - // 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(ptr)); - // We do not accept infinite values. + // 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; +} - // 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::max)() || *outDouble < std::numeric_limits::lowest()); - } +// 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(ptr)); + // We do not accept infinite values. - static bool parse_float_fallback(const uint8_t* ptr, const uint8_t* end_ptr, double* outDouble) { - *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(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::max)() || *outDouble < std::numeric_limits::lowest()); +} - // 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::max)() || *outDouble < std::numeric_limits::lowest()); - } +static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) { + *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(end_ptr)); + // We do not accept infinite values. - // 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); - } + // 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::max)() || *outDouble < std::numeric_limits::lowest()); +} - template - 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(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; - } +// 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); +} - simdjson_inline bool is_digit(const uint8_t c) { - return static_cast(c - '0') <= 9; - } +template +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(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 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; +simdjson_inline bool is_digit(const uint8_t c) { + return static_cast(c - '0') <= 9; +} + +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; #ifdef SIMDJSON_SWAR_NUMBER_PARSING #if SIMDJSON_SWAR_NUMBER_PARSING - // this helps if we have lots of decimals! - // this turns out to be frequent enough. - if (is_made_of_eight_digits_fast(p)) { - i = i * 100000000 + parse_eight_digits_unrolled(p); - p += 8; - } + // this helps if we have lots of decimals! + // this turns out to be frequent enough. + if (is_made_of_eight_digits_fast(p)) { + i = i * 100000000 + parse_eight_digits_unrolled(p); + p += 8; + } #endif // SIMDJSON_SWAR_NUMBER_PARSING #endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING - // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) - if (parse_digit(*p, i)) { ++p; } - while (parse_digit(*p, i)) { p++; } - exponent = first_after_period - p; - // Decimal without digits (123.) is illegal - if (exponent == 0) { - return INVALID_NUMBER(src); - } - return SUCCESS; - } + // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) + if (parse_digit(*p, i)) { ++p; } + while (parse_digit(*p, i)) { p++; } + exponent = first_after_period - p; + // Decimal without digits (123.) is illegal + if (exponent == 0) { + return INVALID_NUMBER(src); + } + return SUCCESS; +} - 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 +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. + // 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. + // 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 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; - } + // 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 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); - } +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 +} // unnamed namespace - /** @private */ - static 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 */ +static 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 - 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; - } +/** @private */ +template +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 - simdjson_inline error_code parse_number(const uint8_t* const src, W& writer); +// 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 +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 +// for performance analysis, it is sometimes useful to skip parsing #ifdef SIMDJSON_SKIPNUMBERPARSING - template - simdjson_inline error_code parse_number(const uint8_t* const, W& writer) { - writer.append_s64(0); // always write zero - return SUCCESS; // always succeeds - } +template +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 parse_unsigned(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline bool is_negative(const uint8_t* src) noexcept { return false; } - simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t* src) noexcept { return false; } - simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t* src) noexcept { return number_type::signed_integer; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t * src) noexcept { return number_type::signed_integer; } #else // parse the number at src @@ -19071,744 +23133,753 @@ namespace simdjson { // content and append a space before calling this function. // // Our objective is accurate parsing (ULP of 0) at high speed. - template - 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)) { 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 INVALID_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 { - WRITE_INTEGER(negative ? (~i + 1) : i, src, writer); - } - 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 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; - 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); - // 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 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 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; - 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); - // 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 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; - 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); - // 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 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 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; - while (parse_digit(*src, i)) { src++; } - - // 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 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; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (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 == '.')) { - p++; - const uint8_t* start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (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 = 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; - } - - simdjson_unused simdjson_inline bool is_negative(const uint8_t* src) noexcept { - return (*src == '-'); - } - - simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t* src) noexcept { - bool negative = (*src == '-'); - src += uint8_t(negative); - const uint8_t* p = src; - while (static_cast(*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 get_number_type(const uint8_t* src) noexcept { - bool negative = (*src == '-'); - src += uint8_t(negative); - const uint8_t* p = src; - while (static_cast(*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("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; - } - 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("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 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 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; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (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 == '.')) { - p++; - const uint8_t* start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (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 = 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; - } - - } // unnamed namespace +template +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)) { 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 INVALID_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 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; + 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); + // 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 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 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; + 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); + // 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 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; + 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); + // 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 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 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; + while (parse_digit(*src, i)) { src++; } + + // 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { + return (*src == '-'); +} + +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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 get_number_type(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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("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("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 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +} // unnamed namespace #endif // SIMDJSON_SKIPNUMBERPARSING - } // namespace numberparsing +} // 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; - } +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 icelake } // namespace simdjson #endif // SIMDJSON_GENERIC_NUMBERPARSING_H @@ -19825,84 +23896,113 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { +namespace icelake { - // - // internal::implementation_simdjson_result_base inline implementation - // +// +// internal::implementation_simdjson_result_base inline implementation +// - template - simdjson_inline void implementation_simdjson_result_base::tie(T& value, error_code& error) && noexcept { - error = this->second; - if (!error) { - value = std::forward>(*this).first; - } - } +template +simdjson_inline void implementation_simdjson_result_base::tie(T &value, error_code &error) && noexcept { + error = this->second; + if (!error) { + value = std::forward>(*this).first; + } +} - template - simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T& value) && noexcept { - error_code error; - std::forward>(*this).tie(value, error); - return error; - } +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T &value) && noexcept { + error_code error; + std::forward>(*this).tie(value, error); + return error; +} - template - simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { - return this->second; - } +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { + return this->second; +} + + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} #if SIMDJSON_EXCEPTIONS - template - simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return this->first; - } +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} - template - simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { - return std::forward>(*this).take_value(); - } +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} - template - simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return std::forward(this->first); - } +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} - template - simdjson_inline implementation_simdjson_result_base::operator T && () && noexcept(false) { - return std::forward>(*this).take_value(); - } + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + +template +simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return this->first; +} + +template +simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { + return std::forward>(*this).take_value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return std::forward(this->first); +} + +template +simdjson_inline implementation_simdjson_result_base::operator T&&() && noexcept(false) { + return std::forward>(*this).take_value(); +} #endif // SIMDJSON_EXCEPTIONS - template - simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { - return this->first; - } +template +simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { + return this->first; +} - template - simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { - return this->first; - } +template +simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { + return this->first; +} - template - simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { - return std::forward(this->first); - } +template +simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { + return std::forward(this->first); +} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T&& value, error_code error) noexcept - : first{ std::forward(value) }, second{ error } {} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept - : implementation_simdjson_result_base(T{}, error) {} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T&& value) noexcept - : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value, error_code error) noexcept + : first{std::forward(value)}, second{error} {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept + : implementation_simdjson_result_base(T{}, error) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value) noexcept + : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} - } // namespace icelake +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H @@ -19944,21 +24044,21 @@ SIMDJSON_UNTARGET_REGION /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - /** - * Implementation for ALTIVEC (PPC64). - */ - namespace ppc64 { +/** + * Implementation for ALTIVEC (PPC64). + */ +namespace ppc64 { - class implementation; +class implementation; - namespace { - namespace simd { - template struct simd8; - template struct simd8x64; - } // namespace simd - } // unnamed namespace +namespace { +namespace simd { +template struct simd8; +template struct simd8x64; +} // namespace simd +} // unnamed namespace - } // namespace ppc64 +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_PPC64_BASE_H @@ -19999,73 +24099,73 @@ static_assert(sizeof(__vector unsigned char) <= simdjson::SIMDJSON_PADDING, "ins /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { - namespace { +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) { +// 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; + 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); + 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 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) { +/* 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; + 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); + 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 - } +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); - } +simdjson_inline int count_ones(uint64_t input_num) { + return __builtin_popcountll(input_num); +} #endif - simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, - uint64_t* result) { +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { #if SIMDJSON_REGULAR_VISUAL_STUDIO - * result = value1 + value2; - return *result < value1; + *result = value1 + value2; + return *result < value1; #else - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); #endif - } +} - } // unnamed namespace - } // namespace ppc64 +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_PPC64_BITMANIPULATION_H @@ -20080,41 +24180,41 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { - namespace { +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; - } +// +// 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 +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif @@ -20139,51 +24239,57 @@ namespace simdjson { #endif namespace simdjson { - namespace ppc64 { - namespace numberparsing { +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)); +// 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); + val = bswap_64(val); #elif defined(__FreeBSD__) - val = bswap64(val); + val = bswap64(val); #endif #endif - val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8; - val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16; - return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32); - } + 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; +/** @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; + // 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 + 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); + __uint128_t r = (static_cast<__uint128_t>(value1)) * value2; + answer.low = uint64_t(r); + answer.high = uint64_t(r >> 64); #endif - return answer; - } + return answer; +} - } // namespace numberparsing - } // namespace ppc64 +} // 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 */ @@ -20201,469 +24307,464 @@ namespace simdjson { #include namespace simdjson { - namespace ppc64 { - namespace { - namespace simd { +namespace ppc64 { +namespace { +namespace simd { - using __m128i = __vector unsigned char; +using __m128i = __vector unsigned char; - template struct base { - __m128i value; +template struct base { + __m128i value; - // Zero constructor - simdjson_inline base() : value{ __m128i() } {} + // Zero constructor + simdjson_inline base() : value{__m128i()} {} - // Conversion from SIMD register - simdjson_inline base(const __m128i _value) : value(_value) {} + // 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; } + // 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(this); - *this_cast = *this_cast | other; - return *this_cast; - } - simdjson_inline Child& operator&=(const Child other) { - auto this_cast = static_cast(this); - *this_cast = *this_cast & other; - return *this_cast; - } - simdjson_inline Child& operator^=(const Child other) { - auto this_cast = static_cast(this); - *this_cast = *this_cast ^ other; - return *this_cast; - } - }; + // 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(this); + *this_cast = *this_cast | other; + return *this_cast; + } + simdjson_inline Child &operator&=(const Child other) { + auto this_cast = static_cast(this); + *this_cast = *this_cast & other; + return *this_cast; + } + simdjson_inline Child &operator^=(const Child other) { + auto this_cast = static_cast(this); + *this_cast = *this_cast ^ other; + return *this_cast; + } +}; - template > - struct base8 : base> { - typedef uint16_t bitmask_t; - typedef uint32_t bitmask2_t; +template > +struct base8 : base> { + typedef uint16_t bitmask_t; + typedef uint32_t bitmask2_t; - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m128i _value) : base>(_value) {} + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m128i _value) : base>(_value) {} - friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { - return (__m128i)vec_cmpeq(lhs.value, (__m128i)rhs); - } + friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { + return (__m128i)vec_cmpeq(lhs.value, (__m128i)rhs); + } - static const int SIZE = sizeof(base>::value); + static const int SIZE = sizeof(base>::value); - template - simdjson_inline simd8 prev(simd8 prev_chunk) const { - __m128i chunk = this->value; + template + simdjson_inline simd8 prev(simd8 prev_chunk) const { + __m128i chunk = this->value; #ifdef __LITTLE_ENDIAN__ - chunk = (__m128i)vec_reve(this->value); - prev_chunk = (__m128i)vec_reve((__m128i)prev_chunk); + 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); + chunk = (__m128i)vec_sld((__m128i)prev_chunk, (__m128i)chunk, 16 - N); #ifdef __LITTLE_ENDIAN__ - chunk = (__m128i)vec_reve((__m128i)chunk); + chunk = (__m128i)vec_reve((__m128i)chunk); #endif - return chunk; - } - }; + return chunk; + } +}; - // SIMD byte mask type (returned by things like eq and gt) - template <> struct simd8 : base8 { - static simdjson_inline simd8 splat(bool _value) { - return (__m128i)vec_splats((unsigned char)(-(!!_value))); - } +// SIMD byte mask type (returned by things like eq and gt) +template <> struct simd8 : base8 { + static simdjson_inline simd8 splat(bool _value) { + return (__m128i)vec_splats((unsigned char)(-(!!_value))); + } - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m128i _value) - : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) - : base8(splat(_value)) {} + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m128i _value) + : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) + : base8(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 }; + 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)); + result = ((__vector unsigned long long)vec_vbpermq((__m128i)this->value, + (__m128i)perm_mask)); #ifdef __LITTLE_ENDIAN__ - return static_cast(result[1]); + return static_cast(result[1]); #else - return static_cast(result[0]); + return static_cast(result[0]); #endif - } - simdjson_inline bool any() const { - return !vec_all_eq(this->value, (__m128i)vec_splats(0)); - } - simdjson_inline simd8 operator~() const { - return this->value ^ (__m128i)splat(true); - } - }; + } + simdjson_inline bool any() const { + return !vec_all_eq(this->value, (__m128i)vec_splats(0)); + } + simdjson_inline simd8 operator~() const { + return this->value ^ (__m128i)splat(true); + } +}; - template struct base8_numeric : base8 { - static simdjson_inline simd8 splat(T value) { - (void)value; - return (__m128i)vec_splats(value); - } - static simdjson_inline simd8 zero() { return splat(0); } - static simdjson_inline simd8 load(const T values[16]) { - return (__m128i)(vec_vsx_ld(0, reinterpret_cast(values))); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdjson_inline simd8 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(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, - v14, v15); - } +template struct base8_numeric : base8 { + static simdjson_inline simd8 splat(T value) { + (void)value; + return (__m128i)vec_splats(value); + } + static simdjson_inline simd8 zero() { return splat(0); } + static simdjson_inline simd8 load(const T values[16]) { + return (__m128i)(vec_vsx_ld(0, reinterpret_cast(values))); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, + v14, v15); + } - simdjson_inline base8_numeric() : base8() {} - simdjson_inline base8_numeric(const __m128i _value) - : base8(_value) {} + simdjson_inline base8_numeric() : base8() {} + simdjson_inline base8_numeric(const __m128i _value) + : base8(_value) {} - // Store to array - simdjson_inline void store(T dst[16]) const { - vec_vsx_st(this->value, 0, reinterpret_cast<__m128i*>(dst)); - } + // 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 operator~() const { return *this ^ 0xFFu; } + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - // Addition/subtraction are the same for signed and unsigned - simdjson_inline simd8 operator+(const simd8 other) const { - return (__m128i)((__m128i)this->value + (__m128i)other); - } - simdjson_inline simd8 operator-(const simd8 other) const { - return (__m128i)((__m128i)this->value - (__m128i)other); - } - simdjson_inline simd8& operator+=(const simd8 other) { - *this = *this + other; - return *static_cast *>(this); - } - simdjson_inline simd8& operator-=(const simd8 other) { - *this = *this - other; - return *static_cast *>(this); - } + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { + return (__m128i)((__m128i)this->value + (__m128i)other); + } + simdjson_inline simd8 operator-(const simd8 other) const { + return (__m128i)((__m128i)this->value - (__m128i)other); + } + simdjson_inline simd8 &operator+=(const simd8 other) { + *this = *this + other; + return *static_cast *>(this); + } + simdjson_inline simd8 &operator-=(const simd8 other) { + *this = *this - other; + return *static_cast *>(this); + } - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior - // for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return (__m128i)vec_perm((__m128i)lookup_table, (__m128i)lookup_table, this->value); - } + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior + // for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 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 compress(uint32_t mask) - // would be sensible, but the AVX ISA makes this kind of approach difficult. - template - 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. + // 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 compress(uint32_t mask) + // would be sensible, but the AVX ISA makes this kind of approach difficult. + template + 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] - }; + __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); + __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 }); + // 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(pshufb_combine_table + pop1 * 8)); - __m128i answer = vec_perm(pruned, (__m128i)vec_splats(0), compactmask); - vec_vsx_st(answer, 0, reinterpret_cast<__m128i*>(output)); - } + // 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(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 - simdjson_inline simd8 - 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::repeat_16( - replace0, replace1, replace2, replace3, replace4, replace5, replace6, - replace7, replace8, replace9, replace10, replace11, replace12, - replace13, replace14, replace15)); - } - }; + template + simdjson_inline simd8 + 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::repeat_16( + replace0, replace1, replace2, replace3, replace4, replace5, replace6, + replace7, replace8, replace9, replace10, replace11, replace12, + replace13, replace14, replace15)); + } +}; - // Signed bytes - template <> struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) - : base8_numeric(_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 - 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(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, - v13, v14, v15); - } +// Signed bytes +template <> struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) + : base8_numeric(_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 + 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(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, + v13, v14, v15); + } - // Order-sensitive comparisons - simdjson_inline simd8 - max_val(const simd8 other) const { - return (__m128i)vec_max((__vector signed char)this->value, + // Order-sensitive comparisons + simdjson_inline simd8 + max_val(const simd8 other) const { + return (__m128i)vec_max((__vector signed char)this->value, (__vector signed char)(__m128i)other); - } - simdjson_inline simd8 - min_val(const simd8 other) const { - return (__m128i)vec_min((__vector signed char)this->value, + } + simdjson_inline simd8 + min_val(const simd8 other) const { + return (__m128i)vec_min((__vector signed char)this->value, (__vector signed char)(__m128i)other); - } - simdjson_inline simd8 - operator>(const simd8 other) const { - return (__m128i)vec_cmpgt((__vector signed char)this->value, - (__vector signed char)(__m128i)other); - } - simdjson_inline simd8 - operator<(const simd8 other) const { - return (__m128i)vec_cmplt((__vector signed char)this->value, - (__vector signed char)(__m128i)other); - } - }; + } + simdjson_inline simd8 + operator>(const simd8 other) const { + return (__m128i)vec_cmpgt((__vector signed char)this->value, + (__vector signed char)(__m128i)other); + } + simdjson_inline simd8 + operator<(const simd8 other) const { + return (__m128i)vec_cmplt((__vector signed char)this->value, + (__vector signed char)(__m128i)other); + } +}; - // Unsigned bytes - template <> struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) - : base8_numeric(_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 - 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(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, - v13, v14, v15); - } +// Unsigned bytes +template <> struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) + : base8_numeric(_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 + 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(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, + v13, v14, v15); + } - // Saturated math - simdjson_inline simd8 - saturating_add(const simd8 other) const { - return (__m128i)vec_adds(this->value, (__m128i)other); - } - simdjson_inline simd8 - saturating_sub(const simd8 other) const { - return (__m128i)vec_subs(this->value, (__m128i)other); - } + // Saturated math + simdjson_inline simd8 + saturating_add(const simd8 other) const { + return (__m128i)vec_adds(this->value, (__m128i)other); + } + simdjson_inline simd8 + saturating_sub(const simd8 other) const { + return (__m128i)vec_subs(this->value, (__m128i)other); + } - // Order-specific operations - simdjson_inline simd8 - max_val(const simd8 other) const { - return (__m128i)vec_max(this->value, (__m128i)other); - } - simdjson_inline simd8 - min_val(const simd8 other) const { - return (__m128i)vec_min(this->value, (__m128i)other); - } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 - gt_bits(const simd8 other) const { - return this->saturating_sub(other); - } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 - lt_bits(const simd8 other) const { - return other.saturating_sub(*this); - } - simdjson_inline simd8 - operator<=(const simd8 other) const { - return other.max_val(*this) == other; - } - simdjson_inline simd8 - operator>=(const simd8 other) const { - return other.min_val(*this) == other; - } - simdjson_inline simd8 - operator>(const simd8 other) const { - return this->gt_bits(other).any_bits_set(); - } - simdjson_inline simd8 - operator<(const simd8 other) const { - return this->gt_bits(other).any_bits_set(); - } + // Order-specific operations + simdjson_inline simd8 + max_val(const simd8 other) const { + return (__m128i)vec_max(this->value, (__m128i)other); + } + simdjson_inline simd8 + min_val(const simd8 other) const { + return (__m128i)vec_min(this->value, (__m128i)other); + } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 + gt_bits(const simd8 other) const { + return this->saturating_sub(other); + } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 + lt_bits(const simd8 other) const { + return other.saturating_sub(*this); + } + simdjson_inline simd8 + operator<=(const simd8 other) const { + return other.max_val(*this) == other; + } + simdjson_inline simd8 + operator>=(const simd8 other) const { + return other.min_val(*this) == other; + } + simdjson_inline simd8 + operator>(const simd8 other) const { + return this->gt_bits(other).any_bits_set(); + } + simdjson_inline simd8 + operator<(const simd8 other) const { + return this->gt_bits(other).any_bits_set(); + } - // Bit-specific operations - simdjson_inline simd8 bits_not_set() const { - return (__m128i)vec_cmpeq(this->value, (__m128i)vec_splats(uint8_t(0))); - } - simdjson_inline simd8 bits_not_set(simd8 bits) const { - return (*this & bits).bits_not_set(); - } - simdjson_inline simd8 any_bits_set() const { - return ~this->bits_not_set(); - } - simdjson_inline simd8 any_bits_set(simd8 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 bits) const { - return vec_all_eq(vec_and(this->value, (__m128i)bits), - (__m128i)vec_splats(0)); - } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { - return !bits_not_set_anywhere(bits); - } - template simdjson_inline simd8 shr() const { - return simd8( - (__m128i)vec_sr(this->value, (__m128i)vec_splat_u8(N))); - } - template simdjson_inline simd8 shl() const { - return simd8( - (__m128i)vec_sl(this->value, (__m128i)vec_splat_u8(N))); - } - }; + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { + return (__m128i)vec_cmpeq(this->value, (__m128i)vec_splats(uint8_t(0))); + } + simdjson_inline simd8 bits_not_set(simd8 bits) const { + return (*this & bits).bits_not_set(); + } + simdjson_inline simd8 any_bits_set() const { + return ~this->bits_not_set(); + } + simdjson_inline simd8 any_bits_set(simd8 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 bits) const { + return vec_all_eq(vec_and(this->value, (__m128i)bits), + (__m128i)vec_splats(0)); + } + simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { + return !bits_not_set_anywhere(bits); + } + template simdjson_inline simd8 shr() const { + return simd8( + (__m128i)vec_sr(this->value, (__m128i)vec_splat_u8(N))); + } + template simdjson_inline simd8 shl() const { + return simd8( + (__m128i)vec_sl(this->value, (__m128i)vec_splat_u8(N))); + } +}; - template struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 4, - "PPC64 kernel should use four registers per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; +template struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 4, + "PPC64 kernel should use four registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& - operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed + simd8x64(const simd8x64 &o) = delete; // no copy allowed + simd8x64 & + operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, - const simd8 chunk2, const simd8 chunk3) - : chunks{ chunk0, chunk1, chunk2, chunk3 } {} - simdjson_inline simd8x64(const T ptr[64]) - : chunks{ simd8::load(ptr), simd8::load(ptr + 16), - simd8::load(ptr + 32), simd8::load(ptr + 48) } {} + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, + const simd8 chunk2, const simd8 chunk3) + : chunks{chunk0, chunk1, chunk2, chunk3} {} + simdjson_inline simd8x64(const T ptr[64]) + : chunks{simd8::load(ptr), simd8::load(ptr + 16), + simd8::load(ptr + 32), simd8::load(ptr + 48)} {} - simdjson_inline void store(T ptr[64]) const { - this->chunks[0].store(ptr + sizeof(simd8) * 0); - this->chunks[1].store(ptr + sizeof(simd8) * 1); - this->chunks[2].store(ptr + sizeof(simd8) * 2); - this->chunks[3].store(ptr + sizeof(simd8) * 3); - } + simdjson_inline void store(T ptr[64]) const { + this->chunks[0].store(ptr + sizeof(simd8) * 0); + this->chunks[1].store(ptr + sizeof(simd8) * 1); + this->chunks[2].store(ptr + sizeof(simd8) * 2); + this->chunks[3].store(ptr + sizeof(simd8) * 3); + } - simdjson_inline simd8 reduce_or() const { - return (this->chunks[0] | this->chunks[1]) | - (this->chunks[2] | this->chunks[3]); - } + simdjson_inline simd8 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 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 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 mask = simd8::splat(m); - return simd8x64(this->chunks[0] == mask, this->chunks[1] == mask, - this->chunks[2] == mask, this->chunks[3] == mask) - .to_bitmask(); - } + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64(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& other) const { - return simd8x64(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 eq(const simd8x64 &other) const { + return simd8x64(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 mask = simd8::splat(m); - return simd8x64(this->chunks[0] <= mask, this->chunks[1] <= mask, - this->chunks[2] <= mask, this->chunks[3] <= mask) - .to_bitmask(); - } - }; // struct simd8x64 + simdjson_inline uint64_t lteq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64(this->chunks[0] <= mask, this->chunks[1] <= mask, + this->chunks[2] <= mask, this->chunks[3] <= mask) + .to_bitmask(); + } +}; // struct simd8x64 - } // namespace simd - } // unnamed namespace - } // namespace ppc64 +} // namespace simd +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_PPC64_SIMD_INPUT_H @@ -20680,58 +24781,84 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { - namespace { +namespace ppc64 { +namespace { - using namespace simd; +using namespace simd; - // Holds backslashes and quotes locations. - struct backslash_and_quote { - public: - static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote - copy_and_find(const uint8_t* src, uint8_t* dst); +// 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); - } + 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 + 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 v0(src); - simd8 v1(src + sizeof(v0)); - v0.store(dst); - v1.store(dst + sizeof(v0)); +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 v0(src); + simd8 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(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); - return { - uint32_t(bs_and_quote), // bs_bits - uint32_t(bs_and_quote >> 32) // quote_bits - }; - } + // 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(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); + return { + uint32_t(bs_and_quote), // bs_bits + uint32_t(bs_and_quote >> 32) // quote_bits + }; +} - } // unnamed namespace - } // namespace ppc64 + +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 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 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 @@ -20766,10 +24893,12 @@ namespace simdjson { /* 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_LSX */ -/* amalgamation skipped (editor-only): #include "simdjson/lsx/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 */ @@ -20779,22 +24908,22 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { +namespace ppc64 { - struct open_container; - class dom_parser_implementation; +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 - }; +/** + * 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 ppc64 } // namespace simdjson #endif // SIMDJSON_GENERIC_BASE_H @@ -20811,99 +24940,97 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { - namespace { - namespace jsoncharutils { +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]; - } +// 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]; - } +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; - } +// 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 - } +// 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; - } +// 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 jsoncharutils +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_GENERIC_JSONCHARUTILS_H @@ -20921,69 +25048,69 @@ namespace simdjson { #include namespace simdjson { - namespace ppc64 { - namespace { - /// @private - namespace atomparsing { +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; } +// 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; } - // 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); - } +// 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); +} - 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) { + 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_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) { + 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_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) { + 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; } - } +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; } +} - } // namespace atomparsing - } // unnamed namespace - } // namespace ppc64 +} // namespace atomparsing +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_GENERIC_ATOMPARSING_H @@ -20999,83 +25126,83 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { +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 +// 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"); +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_containers{}; - /** Whether each open container is a [ or { */ - std::unique_ptr 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{}; +class dom_parser_implementation final : public internal::dom_parser_implementation { +public: + /** Tape location of each open { or [ */ + std::unique_ptr open_containers{}; + /** Whether each open container is a [ or { */ + std::unique_ptr 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; + 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); + 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 ppc64 } // namespace simdjson namespace simdjson { - namespace ppc64 { +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; +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) { return CAPACITY; } - // Stage 1 index output - size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; - 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; +// 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) { return CAPACITY; } + // Stage 1 index output + size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; + 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; - } + _capacity = capacity; + return SUCCESS; +} - inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { - // 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; } +inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { + // 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; - } + _max_depth = max_depth; + return SUCCESS; +} - } // namespace ppc64 +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H @@ -21090,129 +25217,148 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { +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 : public internal::implementation_simdjson_result_base { - * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} - * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} - * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} - * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} - * // Your extra methods here - * } - * - * Then any method returning simdjson_result will be chainable with your methods. - */ - template - struct implementation_simdjson_result_base { +// 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 : public internal::implementation_simdjson_result_base { + * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} + * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} + * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} + * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} + * // Your extra methods here + * } + * + * Then any method returning simdjson_result will be chainable with your methods. + */ +template +struct implementation_simdjson_result_base { - /** - * Create a new empty result with error = UNINITIALIZED. - */ - simdjson_inline implementation_simdjson_result_base() noexcept = default; + /** + * 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 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 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; + /** + * 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 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; + /** + * 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_inline error_code error() const 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& value() & noexcept(false); + /** + * 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); - /** - * Take the result value (move it). - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline T&& value() && 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&& take_value() && noexcept(false); + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& 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); + /** + * 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; - 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 + /** + * 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; - } // namespace ppc64 + 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 @@ -21233,8 +25379,8 @@ namespace simdjson { #include namespace simdjson { - namespace ppc64 { - namespace numberparsing { +namespace ppc64 { +namespace numberparsing { #ifdef JSON_TEST_NUMBERS #define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) @@ -21250,540 +25396,547 @@ namespace simdjson { #define BIGINT_NUMBER(SRC) (BIGINT_ERROR) #endif - namespace { +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(negative)) << 63); - std::memcpy(&d, &mantissa, sizeof(d)); - return d; - } +// 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(negative)) << 63); + std::memcpy(&d, &mantissa, sizeof(d)); + return d; +} - // 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 +// 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) + if (0 <= power && power <= 22 && i <= 9007199254740991) #else - if (-22 <= power && power <= 22 && i <= 9007199254740991) + 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 + { + // 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. + // 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; - } + // 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 - // approximatively 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; + // 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 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. - simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]); - // 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. + // 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 - // 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. - simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); - 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); + // 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. - // 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 - } - } + // 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); - mantissa += mantissa & 1; - mantissa >>= 1; + // 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 + } + } - // 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; - } + mantissa += mantissa & 1; + mantissa >>= 1; - // 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(ptr)); - // We do not accept infinite values. + // 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; +} - // 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::max)() || *outDouble < std::numeric_limits::lowest()); - } +// 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(ptr)); + // We do not accept infinite values. - static bool parse_float_fallback(const uint8_t* ptr, const uint8_t* end_ptr, double* outDouble) { - *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(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::max)() || *outDouble < std::numeric_limits::lowest()); +} - // 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::max)() || *outDouble < std::numeric_limits::lowest()); - } +static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) { + *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(end_ptr)); + // We do not accept infinite values. - // 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); - } + // 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::max)() || *outDouble < std::numeric_limits::lowest()); +} - template - 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(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; - } +// 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); +} - simdjson_inline bool is_digit(const uint8_t c) { - return static_cast(c - '0') <= 9; - } +template +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(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 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; +simdjson_inline bool is_digit(const uint8_t c) { + return static_cast(c - '0') <= 9; +} + +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; #ifdef SIMDJSON_SWAR_NUMBER_PARSING #if SIMDJSON_SWAR_NUMBER_PARSING - // this helps if we have lots of decimals! - // this turns out to be frequent enough. - if (is_made_of_eight_digits_fast(p)) { - i = i * 100000000 + parse_eight_digits_unrolled(p); - p += 8; - } + // this helps if we have lots of decimals! + // this turns out to be frequent enough. + if (is_made_of_eight_digits_fast(p)) { + i = i * 100000000 + parse_eight_digits_unrolled(p); + p += 8; + } #endif // SIMDJSON_SWAR_NUMBER_PARSING #endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING - // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) - if (parse_digit(*p, i)) { ++p; } - while (parse_digit(*p, i)) { p++; } - exponent = first_after_period - p; - // Decimal without digits (123.) is illegal - if (exponent == 0) { - return INVALID_NUMBER(src); - } - return SUCCESS; - } + // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) + if (parse_digit(*p, i)) { ++p; } + while (parse_digit(*p, i)) { p++; } + exponent = first_after_period - p; + // Decimal without digits (123.) is illegal + if (exponent == 0) { + return INVALID_NUMBER(src); + } + return SUCCESS; +} - 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 +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. + // 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. + // 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 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; - } + // 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 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); - } +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 +} // unnamed namespace - /** @private */ - static 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 */ +static 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 - 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; - } +/** @private */ +template +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 - simdjson_inline error_code parse_number(const uint8_t* const src, W& writer); +// 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 +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 +// for performance analysis, it is sometimes useful to skip parsing #ifdef SIMDJSON_SKIPNUMBERPARSING - template - simdjson_inline error_code parse_number(const uint8_t* const, W& writer) { - writer.append_s64(0); // always write zero - return SUCCESS; // always succeeds - } +template +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 parse_unsigned(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline bool is_negative(const uint8_t* src) noexcept { return false; } - simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t* src) noexcept { return false; } - simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t* src) noexcept { return number_type::signed_integer; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t * src) noexcept { return number_type::signed_integer; } #else // parse the number at src @@ -21795,744 +25948,753 @@ namespace simdjson { // content and append a space before calling this function. // // Our objective is accurate parsing (ULP of 0) at high speed. - template - 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)) { 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 INVALID_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 { - WRITE_INTEGER(negative ? (~i + 1) : i, src, writer); - } - 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 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; - 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); - // 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 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 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; - 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); - // 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 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; - 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); - // 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 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 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; - while (parse_digit(*src, i)) { src++; } - - // 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 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; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (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 == '.')) { - p++; - const uint8_t* start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (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 = 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; - } - - simdjson_unused simdjson_inline bool is_negative(const uint8_t* src) noexcept { - return (*src == '-'); - } - - simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t* src) noexcept { - bool negative = (*src == '-'); - src += uint8_t(negative); - const uint8_t* p = src; - while (static_cast(*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 get_number_type(const uint8_t* src) noexcept { - bool negative = (*src == '-'); - src += uint8_t(negative); - const uint8_t* p = src; - while (static_cast(*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("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; - } - 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("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 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 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; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (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 == '.')) { - p++; - const uint8_t* start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (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 = 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; - } - - } // unnamed namespace +template +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)) { 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 INVALID_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 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; + 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); + // 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 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 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; + 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); + // 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 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; + 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); + // 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 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 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; + while (parse_digit(*src, i)) { src++; } + + // 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { + return (*src == '-'); +} + +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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 get_number_type(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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("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("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 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +} // unnamed namespace #endif // SIMDJSON_SKIPNUMBERPARSING - } // namespace numberparsing +} // 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; - } +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 ppc64 } // namespace simdjson #endif // SIMDJSON_GENERIC_NUMBERPARSING_H @@ -22549,84 +26711,113 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { +namespace ppc64 { - // - // internal::implementation_simdjson_result_base inline implementation - // +// +// internal::implementation_simdjson_result_base inline implementation +// - template - simdjson_inline void implementation_simdjson_result_base::tie(T& value, error_code& error) && noexcept { - error = this->second; - if (!error) { - value = std::forward>(*this).first; - } - } +template +simdjson_inline void implementation_simdjson_result_base::tie(T &value, error_code &error) && noexcept { + error = this->second; + if (!error) { + value = std::forward>(*this).first; + } +} - template - simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T& value) && noexcept { - error_code error; - std::forward>(*this).tie(value, error); - return error; - } +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T &value) && noexcept { + error_code error; + std::forward>(*this).tie(value, error); + return error; +} - template - simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { - return this->second; - } +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { + return this->second; +} + + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} #if SIMDJSON_EXCEPTIONS - template - simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return this->first; - } +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} - template - simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { - return std::forward>(*this).take_value(); - } +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} - template - simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return std::forward(this->first); - } +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} - template - simdjson_inline implementation_simdjson_result_base::operator T && () && noexcept(false) { - return std::forward>(*this).take_value(); - } + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + +template +simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return this->first; +} + +template +simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { + return std::forward>(*this).take_value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return std::forward(this->first); +} + +template +simdjson_inline implementation_simdjson_result_base::operator T&&() && noexcept(false) { + return std::forward>(*this).take_value(); +} #endif // SIMDJSON_EXCEPTIONS - template - simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { - return this->first; - } +template +simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { + return this->first; +} - template - simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { - return this->first; - } +template +simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { + return this->first; +} - template - simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { - return std::forward(this->first); - } +template +simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { + return std::forward(this->first); +} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T&& value, error_code error) noexcept - : first{ std::forward(value) }, second{ error } {} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept - : implementation_simdjson_result_base(T{}, error) {} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T&& value) noexcept - : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value, error_code error) noexcept + : first{std::forward(value)}, second{error} {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept + : implementation_simdjson_result_base(T{}, error) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value) noexcept + : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} - } // namespace ppc64 +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H @@ -22666,23 +26857,23 @@ namespace simdjson { // 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 { +/** + * Implementation for Westmere (Intel SSE4.2). + */ +namespace westmere { - class implementation; +class implementation; - namespace { - namespace simd { +namespace { +namespace simd { - template struct simd8; - template struct simd8x64; +template struct simd8; +template struct simd8x64; - } // namespace simd - } // unnamed namespace +} // namespace simd +} // unnamed namespace - } // namespace westmere +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_WESTMERE_BASE_H @@ -22737,73 +26928,73 @@ SIMDJSON_TARGET_REGION("sse4.2,pclmul,popcnt") /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { +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) { +// 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; + 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); + 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 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) { +/* 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; + 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); + 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 - } +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); - } +simdjson_inline long long int count_ones(uint64_t input_num) { + return _popcnt64(input_num); +} #endif - simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, - uint64_t* result) { +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { #if SIMDJSON_REGULAR_VISUAL_STUDIO - return _addcarry_u64(0, value1, value2, - reinterpret_cast(result)); + return _addcarry_u64(0, value1, value2, + reinterpret_cast(result)); #else - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); #endif - } +} - } // unnamed namespace - } // namespace westmere +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_WESTMERE_BITMANIPULATION_H @@ -22819,24 +27010,24 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { +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); - } +// +// 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 +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_WESTMERE_BITMASK_H @@ -22857,23 +27048,23 @@ namespace simdjson { // 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 { +/** + * Implementation for Westmere (Intel SSE4.2). + */ +namespace westmere { - class implementation; +class implementation; - namespace { - namespace simd { +namespace { +namespace simd { - template struct simd8; - template struct simd8x64; +template struct simd8; +template struct simd8x64; - } // namespace simd - } // unnamed namespace +} // namespace simd +} // unnamed namespace - } // namespace westmere +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_WESTMERE_BASE_H @@ -22918,49 +27109,49 @@ static_assert(sizeof(__m128i) <= simdjson::SIMDJSON_PADDING, "insufficient paddi /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace numberparsing { +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(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 */ +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(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; +/** @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; + // 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 + 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); + __uint128_t r = (static_cast<__uint128_t>(value1)) * value2; + answer.low = uint64_t(r); + answer.high = uint64_t(r >> 64); #endif - return answer; - } + return answer; +} - } // namespace numberparsing - } // namespace westmere +} // namespace numberparsing +} // namespace westmere } // namespace simdjson #define SIMDJSON_SWAR_NUMBER_PARSING 1 @@ -22979,331 +27170,332 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { - namespace simd { +namespace westmere { +namespace { +namespace simd { - template - struct base { - __m128i value; + template + struct base { + __m128i value; - // Zero constructor - simdjson_inline base() : value{ __m128i() } {} + // Zero constructor + simdjson_inline base() : value{__m128i()} {} - // Conversion from SIMD register - simdjson_inline base(const __m128i _value) : value(_value) {} + // 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; } + // 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(this); *this_cast = *this_cast | other; return *this_cast; } - simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } - simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } - }; + // 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(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; - template> - struct base8 : base> { - typedef uint16_t bitmask_t; - typedef uint32_t bitmask2_t; + template> + struct base8: base> { + typedef uint16_t bitmask_t; + typedef uint32_t bitmask2_t; - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m128i _value) : base>(_value) {} + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m128i _value) : base>(_value) {} - friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm_cmpeq_epi8(lhs, rhs); } + friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm_cmpeq_epi8(lhs, rhs); } - static const int SIZE = sizeof(base>::value); + static const int SIZE = sizeof(base>::value); - template - simdjson_inline simd8 prev(const simd8 prev_chunk) const { - return _mm_alignr_epi8(*this, prev_chunk, 16 - N); - } - }; + template + simdjson_inline simd8 prev(const simd8 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 : base8 { - static simdjson_inline simd8 splat(bool _value) { return _mm_set1_epi8(uint8_t(-(!!_value))); } + // SIMD byte mask type (returned by things like eq and gt) + template<> + struct simd8: base8 { + static simdjson_inline simd8 splat(bool _value) { return _mm_set1_epi8(uint8_t(-(!!_value))); } - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m128i _value) : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) : base8(splat(_value)) {} + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m128i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(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 operator~() const { return *this ^ true; } - }; + 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 operator~() const { return *this ^ true; } + }; - template - struct base8_numeric : base8 { - static simdjson_inline simd8 splat(T _value) { return _mm_set1_epi8(_value); } - static simdjson_inline simd8 zero() { return _mm_setzero_si128(); } - static simdjson_inline simd8 load(const T values[16]) { - return _mm_loadu_si128(reinterpret_cast(values)); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdjson_inline simd8 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + template + struct base8_numeric: base8 { + static simdjson_inline simd8 splat(T _value) { return _mm_set1_epi8(_value); } + static simdjson_inline simd8 zero() { return _mm_setzero_si128(); } + static simdjson_inline simd8 load(const T values[16]) { + return _mm_loadu_si128(reinterpret_cast(values)); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - simdjson_inline base8_numeric() : base8() {} - simdjson_inline base8_numeric(const __m128i _value) : base8(_value) {} + simdjson_inline base8_numeric() : base8() {} + simdjson_inline base8_numeric(const __m128i _value) : base8(_value) {} - // Store to array - simdjson_inline void store(T dst[16]) const { return _mm_storeu_si128(reinterpret_cast<__m128i*>(dst), *this); } + // 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 operator~() const { return *this ^ 0xFFu; } + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - // Addition/subtraction are the same for signed and unsigned - simdjson_inline simd8 operator+(const simd8 other) const { return _mm_add_epi8(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return _mm_sub_epi8(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { return _mm_add_epi8(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return _mm_sub_epi8(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return _mm_shuffle_epi8(lookup_table, *this); - } + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 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 compress(uint32_t mask) would be - // sensible, but the AVX ISA makes this kind of approach difficult. - template - 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(pshufb_combine_table + pop1 * 8)); - __m128i answer = _mm_shuffle_epi8(pruned, compactmask); - _mm_storeu_si128(reinterpret_cast<__m128i*>(output), answer); - } + // 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 compress(uint32_t mask) would be + // sensible, but the AVX ISA makes this kind of approach difficult. + template + 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(pshufb_combine_table + pop1 * 8)); + __m128i answer = _mm_shuffle_epi8(pruned, compactmask); + _mm_storeu_si128(reinterpret_cast<__m128i *>(output), answer); + } - template - simdjson_inline simd8 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::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - }; + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; - // Signed bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + // Signed bytes + template<> + struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - // Order-sensitive comparisons - simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epi8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epi8(*this, other); } - simdjson_inline simd8 operator>(const simd8 other) const { return _mm_cmpgt_epi8(*this, other); } - simdjson_inline simd8 operator<(const simd8 other) const { return _mm_cmpgt_epi8(other, *this); } - }; + // Order-sensitive comparisons + simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epi8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epi8(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return _mm_cmpgt_epi8(*this, other); } + simdjson_inline simd8 operator<(const simd8 other) const { return _mm_cmpgt_epi8(other, *this); } + }; - // Unsigned bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + // Unsigned bytes + template<> + struct simd8: base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - // Saturated math - simdjson_inline simd8 saturating_add(const simd8 other) const { return _mm_adds_epu8(*this, other); } - simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm_subs_epu8(*this, other); } + // Saturated math + simdjson_inline simd8 saturating_add(const simd8 other) const { return _mm_adds_epu8(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm_subs_epu8(*this, other); } - // Order-specific operations - simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epu8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epu8(*this, other); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } - simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } - simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } - simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - simdjson_inline simd8 operator<(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + // Order-specific operations + simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epu8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epu8(*this, other); } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } + simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } + simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } + simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + simdjson_inline simd8 operator<(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - // Bit-specific operations - simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } - simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } - simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } - simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return _mm_testz_si128(*this, bits); } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } - template - simdjson_inline simd8 shr() const { return simd8(_mm_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } - template - simdjson_inline simd8 shl() const { return simd8(_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 - simdjson_inline int get_bit() const { return _mm_movemask_epi8(_mm_slli_epi16(*this, 7 - N)); } - }; + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } + simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } + simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } + simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return _mm_testz_si128(*this, bits); } + simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } + template + simdjson_inline simd8 shr() const { return simd8(_mm_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } + template + simdjson_inline simd8 shl() const { return simd8(_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 + simdjson_inline int get_bit() const { return _mm_movemask_epi8(_mm_slli_epi16(*this, 7-N)); } + }; - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 4, "Westmere kernel should use four registers per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 4, "Westmere kernel should use four registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{ chunk0, chunk1, chunk2, chunk3 } {} - simdjson_inline simd8x64(const T ptr[64]) : chunks{ simd8::load(ptr), simd8::load(ptr + 16), simd8::load(ptr + 32), simd8::load(ptr + 48) } {} + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::load(ptr+16), simd8::load(ptr+32), simd8::load(ptr+48)} {} - simdjson_inline void store(T ptr[64]) const { - this->chunks[0].store(ptr + sizeof(simd8) * 0); - this->chunks[1].store(ptr + sizeof(simd8) * 1); - this->chunks[2].store(ptr + sizeof(simd8) * 2); - this->chunks[3].store(ptr + sizeof(simd8) * 3); - } + simdjson_inline void store(T ptr[64]) const { + this->chunks[0].store(ptr+sizeof(simd8)*0); + this->chunks[1].store(ptr+sizeof(simd8)*1); + this->chunks[2].store(ptr+sizeof(simd8)*2); + this->chunks[3].store(ptr+sizeof(simd8)*3); + } - simdjson_inline simd8 reduce_or() const { - return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]); - } + simdjson_inline simd8 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 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 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] == mask, - this->chunks[1] == mask, - this->chunks[2] == mask, - this->chunks[3] == mask - ).to_bitmask(); - } + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + 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& other) const { - return simd8x64( - 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 eq(const simd8x64 &other) const { + return simd8x64( + 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] <= mask, - this->chunks[1] <= mask, - this->chunks[2] <= mask, - this->chunks[3] <= mask - ).to_bitmask(); - } - }; // struct simd8x64 + simdjson_inline uint64_t lteq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] <= mask, + this->chunks[1] <= mask, + this->chunks[2] <= mask, + this->chunks[3] <= mask + ).to_bitmask(); + } + }; // struct simd8x64 - } // namespace simd - } // unnamed namespace - } // namespace westmere +} // namespace simd +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_WESTMERE_SIMD_INPUT_H @@ -23324,73 +27516,73 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { +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) { +// 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; + 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); + 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 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) { +/* 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; + 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); + 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 - } +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); - } +simdjson_inline long long int count_ones(uint64_t input_num) { + return _popcnt64(input_num); +} #endif - simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, - uint64_t* result) { +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { #if SIMDJSON_REGULAR_VISUAL_STUDIO - return _addcarry_u64(0, value1, value2, - reinterpret_cast(result)); + return _addcarry_u64(0, value1, value2, + reinterpret_cast(result)); #else - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); #endif - } +} - } // unnamed namespace - } // namespace westmere +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_WESTMERE_BITMANIPULATION_H @@ -23407,374 +27599,400 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { - namespace simd { +namespace westmere { +namespace { +namespace simd { - template - struct base { - __m128i value; + template + struct base { + __m128i value; - // Zero constructor - simdjson_inline base() : value{ __m128i() } {} + // Zero constructor + simdjson_inline base() : value{__m128i()} {} - // Conversion from SIMD register - simdjson_inline base(const __m128i _value) : value(_value) {} + // 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; } + // 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(this); *this_cast = *this_cast | other; return *this_cast; } - simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } - simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } - }; + // 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(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; - template> - struct base8 : base> { - typedef uint16_t bitmask_t; - typedef uint32_t bitmask2_t; + template> + struct base8: base> { + typedef uint16_t bitmask_t; + typedef uint32_t bitmask2_t; - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m128i _value) : base>(_value) {} + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m128i _value) : base>(_value) {} - friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm_cmpeq_epi8(lhs, rhs); } + friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm_cmpeq_epi8(lhs, rhs); } - static const int SIZE = sizeof(base>::value); + static const int SIZE = sizeof(base>::value); - template - simdjson_inline simd8 prev(const simd8 prev_chunk) const { - return _mm_alignr_epi8(*this, prev_chunk, 16 - N); - } - }; + template + simdjson_inline simd8 prev(const simd8 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 : base8 { - static simdjson_inline simd8 splat(bool _value) { return _mm_set1_epi8(uint8_t(-(!!_value))); } + // SIMD byte mask type (returned by things like eq and gt) + template<> + struct simd8: base8 { + static simdjson_inline simd8 splat(bool _value) { return _mm_set1_epi8(uint8_t(-(!!_value))); } - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m128i _value) : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) : base8(splat(_value)) {} + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m128i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(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 operator~() const { return *this ^ true; } - }; + 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 operator~() const { return *this ^ true; } + }; - template - struct base8_numeric : base8 { - static simdjson_inline simd8 splat(T _value) { return _mm_set1_epi8(_value); } - static simdjson_inline simd8 zero() { return _mm_setzero_si128(); } - static simdjson_inline simd8 load(const T values[16]) { - return _mm_loadu_si128(reinterpret_cast(values)); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdjson_inline simd8 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + template + struct base8_numeric: base8 { + static simdjson_inline simd8 splat(T _value) { return _mm_set1_epi8(_value); } + static simdjson_inline simd8 zero() { return _mm_setzero_si128(); } + static simdjson_inline simd8 load(const T values[16]) { + return _mm_loadu_si128(reinterpret_cast(values)); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - simdjson_inline base8_numeric() : base8() {} - simdjson_inline base8_numeric(const __m128i _value) : base8(_value) {} + simdjson_inline base8_numeric() : base8() {} + simdjson_inline base8_numeric(const __m128i _value) : base8(_value) {} - // Store to array - simdjson_inline void store(T dst[16]) const { return _mm_storeu_si128(reinterpret_cast<__m128i*>(dst), *this); } + // 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 operator~() const { return *this ^ 0xFFu; } + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - // Addition/subtraction are the same for signed and unsigned - simdjson_inline simd8 operator+(const simd8 other) const { return _mm_add_epi8(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return _mm_sub_epi8(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { return _mm_add_epi8(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return _mm_sub_epi8(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return _mm_shuffle_epi8(lookup_table, *this); - } + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 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 compress(uint32_t mask) would be - // sensible, but the AVX ISA makes this kind of approach difficult. - template - 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(pshufb_combine_table + pop1 * 8)); - __m128i answer = _mm_shuffle_epi8(pruned, compactmask); - _mm_storeu_si128(reinterpret_cast<__m128i*>(output), answer); - } + // 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 compress(uint32_t mask) would be + // sensible, but the AVX ISA makes this kind of approach difficult. + template + 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(pshufb_combine_table + pop1 * 8)); + __m128i answer = _mm_shuffle_epi8(pruned, compactmask); + _mm_storeu_si128(reinterpret_cast<__m128i *>(output), answer); + } - template - simdjson_inline simd8 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::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - }; + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; - // Signed bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + // Signed bytes + template<> + struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - // Order-sensitive comparisons - simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epi8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epi8(*this, other); } - simdjson_inline simd8 operator>(const simd8 other) const { return _mm_cmpgt_epi8(*this, other); } - simdjson_inline simd8 operator<(const simd8 other) const { return _mm_cmpgt_epi8(other, *this); } - }; + // Order-sensitive comparisons + simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epi8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epi8(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return _mm_cmpgt_epi8(*this, other); } + simdjson_inline simd8 operator<(const simd8 other) const { return _mm_cmpgt_epi8(other, *this); } + }; - // Unsigned bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + // Unsigned bytes + template<> + struct simd8: base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - // Saturated math - simdjson_inline simd8 saturating_add(const simd8 other) const { return _mm_adds_epu8(*this, other); } - simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm_subs_epu8(*this, other); } + // Saturated math + simdjson_inline simd8 saturating_add(const simd8 other) const { return _mm_adds_epu8(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm_subs_epu8(*this, other); } - // Order-specific operations - simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epu8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epu8(*this, other); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } - simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } - simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } - simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - simdjson_inline simd8 operator<(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + // Order-specific operations + simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epu8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epu8(*this, other); } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } + simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } + simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } + simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + simdjson_inline simd8 operator<(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - // Bit-specific operations - simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } - simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } - simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } - simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return _mm_testz_si128(*this, bits); } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } - template - simdjson_inline simd8 shr() const { return simd8(_mm_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } - template - simdjson_inline simd8 shl() const { return simd8(_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 - simdjson_inline int get_bit() const { return _mm_movemask_epi8(_mm_slli_epi16(*this, 7 - N)); } - }; + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } + simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } + simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } + simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return _mm_testz_si128(*this, bits); } + simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } + template + simdjson_inline simd8 shr() const { return simd8(_mm_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } + template + simdjson_inline simd8 shl() const { return simd8(_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 + simdjson_inline int get_bit() const { return _mm_movemask_epi8(_mm_slli_epi16(*this, 7-N)); } + }; - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 4, "Westmere kernel should use four registers per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 4, "Westmere kernel should use four registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{ chunk0, chunk1, chunk2, chunk3 } {} - simdjson_inline simd8x64(const T ptr[64]) : chunks{ simd8::load(ptr), simd8::load(ptr + 16), simd8::load(ptr + 32), simd8::load(ptr + 48) } {} + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::load(ptr+16), simd8::load(ptr+32), simd8::load(ptr+48)} {} - simdjson_inline void store(T ptr[64]) const { - this->chunks[0].store(ptr + sizeof(simd8) * 0); - this->chunks[1].store(ptr + sizeof(simd8) * 1); - this->chunks[2].store(ptr + sizeof(simd8) * 2); - this->chunks[3].store(ptr + sizeof(simd8) * 3); - } + simdjson_inline void store(T ptr[64]) const { + this->chunks[0].store(ptr+sizeof(simd8)*0); + this->chunks[1].store(ptr+sizeof(simd8)*1); + this->chunks[2].store(ptr+sizeof(simd8)*2); + this->chunks[3].store(ptr+sizeof(simd8)*3); + } - simdjson_inline simd8 reduce_or() const { - return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]); - } + simdjson_inline simd8 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 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 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] == mask, - this->chunks[1] == mask, - this->chunks[2] == mask, - this->chunks[3] == mask - ).to_bitmask(); - } + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + 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& other) const { - return simd8x64( - 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 eq(const simd8x64 &other) const { + return simd8x64( + 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] <= mask, - this->chunks[1] <= mask, - this->chunks[2] <= mask, - this->chunks[3] <= mask - ).to_bitmask(); - } - }; // struct simd8x64 + simdjson_inline uint64_t lteq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] <= mask, + this->chunks[1] <= mask, + this->chunks[2] <= mask, + this->chunks[3] <= mask + ).to_bitmask(); + } + }; // struct simd8x64 - } // namespace simd - } // unnamed namespace - } // namespace westmere +} // 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 { +namespace westmere { +namespace { - using namespace simd; +using namespace simd; - // Holds backslashes and quotes locations. - struct backslash_and_quote { - public: - static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t* src, uint8_t* dst); +// 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); } + 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 + 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 v0(src); - simd8 v1(src + 16); - v0.store(dst); - v1.store(dst + 16); - uint64_t bs_and_quote = simd8x64(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); - return { - uint32_t(bs_and_quote), // bs_bits - uint32_t(bs_and_quote >> 32) // quote_bits - }; - } +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 v0(src); + simd8 v1(src + 16); + v0.store(dst); + v1.store(dst + 16); + uint64_t bs_and_quote = simd8x64(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); + return { + uint32_t(bs_and_quote), // bs_bits + uint32_t(bs_and_quote >> 32) // quote_bits + }; +} - } // unnamed namespace - } // namespace westmere + +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 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 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 @@ -23807,10 +28025,12 @@ namespace simdjson { /* 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_LSX */ -/* amalgamation skipped (editor-only): #include "simdjson/lsx/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 */ @@ -23820,22 +28040,22 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { +namespace westmere { - struct open_container; - class dom_parser_implementation; +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 - }; +/** + * 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 westmere } // namespace simdjson #endif // SIMDJSON_GENERIC_BASE_H @@ -23852,99 +28072,97 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { - namespace jsoncharutils { +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]; - } +// 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]; - } +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; - } +// 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 - } +// 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; - } +// 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 jsoncharutils +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_GENERIC_JSONCHARUTILS_H @@ -23962,69 +28180,69 @@ namespace simdjson { #include namespace simdjson { - namespace westmere { - namespace { - /// @private - namespace atomparsing { +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; } +// 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; } - // 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); - } +// 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); +} - 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) { + 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_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) { + 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_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) { + 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; } - } +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; } +} - } // namespace atomparsing - } // unnamed namespace - } // namespace westmere +} // namespace atomparsing +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_GENERIC_ATOMPARSING_H @@ -24040,83 +28258,83 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { +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 +// 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"); +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_containers{}; - /** Whether each open container is a [ or { */ - std::unique_ptr 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{}; +class dom_parser_implementation final : public internal::dom_parser_implementation { +public: + /** Tape location of each open { or [ */ + std::unique_ptr open_containers{}; + /** Whether each open container is a [ or { */ + std::unique_ptr 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; + 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); + 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 westmere } // namespace simdjson namespace simdjson { - namespace westmere { +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; +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) { return CAPACITY; } - // Stage 1 index output - size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; - 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; +// 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) { return CAPACITY; } + // Stage 1 index output + size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; + 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; - } + _capacity = capacity; + return SUCCESS; +} - inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { - // 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; } +inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { + // 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; - } + _max_depth = max_depth; + return SUCCESS; +} - } // namespace westmere +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H @@ -24131,129 +28349,148 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { +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 : public internal::implementation_simdjson_result_base { - * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} - * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} - * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} - * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} - * // Your extra methods here - * } - * - * Then any method returning simdjson_result will be chainable with your methods. - */ - template - struct implementation_simdjson_result_base { +// 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 : public internal::implementation_simdjson_result_base { + * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} + * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} + * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} + * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} + * // Your extra methods here + * } + * + * Then any method returning simdjson_result will be chainable with your methods. + */ +template +struct implementation_simdjson_result_base { - /** - * Create a new empty result with error = UNINITIALIZED. - */ - simdjson_inline implementation_simdjson_result_base() noexcept = default; + /** + * 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 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 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; + /** + * 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 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; + /** + * 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_inline error_code error() const 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& value() & noexcept(false); + /** + * 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); - /** - * Take the result value (move it). - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline T&& value() && 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&& take_value() && noexcept(false); + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& 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); + /** + * 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; - 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 + /** + * 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; - } // namespace westmere + 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 @@ -24274,8 +28511,8 @@ namespace simdjson { #include namespace simdjson { - namespace westmere { - namespace numberparsing { +namespace westmere { +namespace numberparsing { #ifdef JSON_TEST_NUMBERS #define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) @@ -24291,540 +28528,547 @@ namespace simdjson { #define BIGINT_NUMBER(SRC) (BIGINT_ERROR) #endif - namespace { +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(negative)) << 63); - std::memcpy(&d, &mantissa, sizeof(d)); - return d; - } +// 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(negative)) << 63); + std::memcpy(&d, &mantissa, sizeof(d)); + return d; +} - // 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 +// 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) + if (0 <= power && power <= 22 && i <= 9007199254740991) #else - if (-22 <= power && power <= 22 && i <= 9007199254740991) + 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 + { + // 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. + // 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; - } + // 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 - // approximatively 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; + // 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 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. - simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]); - // 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. + // 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 - // 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. - simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); - 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); + // 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. - // 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 - } - } + // 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); - mantissa += mantissa & 1; - mantissa >>= 1; + // 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 + } + } - // 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; - } + mantissa += mantissa & 1; + mantissa >>= 1; - // 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(ptr)); - // We do not accept infinite values. + // 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; +} - // 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::max)() || *outDouble < std::numeric_limits::lowest()); - } +// 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(ptr)); + // We do not accept infinite values. - static bool parse_float_fallback(const uint8_t* ptr, const uint8_t* end_ptr, double* outDouble) { - *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(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::max)() || *outDouble < std::numeric_limits::lowest()); +} - // 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::max)() || *outDouble < std::numeric_limits::lowest()); - } +static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) { + *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(end_ptr)); + // We do not accept infinite values. - // 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); - } + // 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::max)() || *outDouble < std::numeric_limits::lowest()); +} - template - 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(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; - } +// 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); +} - simdjson_inline bool is_digit(const uint8_t c) { - return static_cast(c - '0') <= 9; - } +template +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(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 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; +simdjson_inline bool is_digit(const uint8_t c) { + return static_cast(c - '0') <= 9; +} + +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; #ifdef SIMDJSON_SWAR_NUMBER_PARSING #if SIMDJSON_SWAR_NUMBER_PARSING - // this helps if we have lots of decimals! - // this turns out to be frequent enough. - if (is_made_of_eight_digits_fast(p)) { - i = i * 100000000 + parse_eight_digits_unrolled(p); - p += 8; - } + // this helps if we have lots of decimals! + // this turns out to be frequent enough. + if (is_made_of_eight_digits_fast(p)) { + i = i * 100000000 + parse_eight_digits_unrolled(p); + p += 8; + } #endif // SIMDJSON_SWAR_NUMBER_PARSING #endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING - // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) - if (parse_digit(*p, i)) { ++p; } - while (parse_digit(*p, i)) { p++; } - exponent = first_after_period - p; - // Decimal without digits (123.) is illegal - if (exponent == 0) { - return INVALID_NUMBER(src); - } - return SUCCESS; - } + // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) + if (parse_digit(*p, i)) { ++p; } + while (parse_digit(*p, i)) { p++; } + exponent = first_after_period - p; + // Decimal without digits (123.) is illegal + if (exponent == 0) { + return INVALID_NUMBER(src); + } + return SUCCESS; +} - 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 +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. + // 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. + // 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 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; - } + // 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 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); - } +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 +} // unnamed namespace - /** @private */ - static 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 */ +static 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 - 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; - } +/** @private */ +template +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 - simdjson_inline error_code parse_number(const uint8_t* const src, W& writer); +// 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 +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 +// for performance analysis, it is sometimes useful to skip parsing #ifdef SIMDJSON_SKIPNUMBERPARSING - template - simdjson_inline error_code parse_number(const uint8_t* const, W& writer) { - writer.append_s64(0); // always write zero - return SUCCESS; // always succeeds - } +template +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 parse_unsigned(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline bool is_negative(const uint8_t* src) noexcept { return false; } - simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t* src) noexcept { return false; } - simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t* src) noexcept { return number_type::signed_integer; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t * src) noexcept { return number_type::signed_integer; } #else // parse the number at src @@ -24836,744 +29080,753 @@ namespace simdjson { // content and append a space before calling this function. // // Our objective is accurate parsing (ULP of 0) at high speed. - template - 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)) { 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 INVALID_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 { - WRITE_INTEGER(negative ? (~i + 1) : i, src, writer); - } - 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 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; - 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); - // 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 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 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; - 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); - // 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 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; - 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); - // 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 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 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; - while (parse_digit(*src, i)) { src++; } - - // 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 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; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (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 == '.')) { - p++; - const uint8_t* start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (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 = 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; - } - - simdjson_unused simdjson_inline bool is_negative(const uint8_t* src) noexcept { - return (*src == '-'); - } - - simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t* src) noexcept { - bool negative = (*src == '-'); - src += uint8_t(negative); - const uint8_t* p = src; - while (static_cast(*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 get_number_type(const uint8_t* src) noexcept { - bool negative = (*src == '-'); - src += uint8_t(negative); - const uint8_t* p = src; - while (static_cast(*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("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; - } - 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("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 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 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; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (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 == '.')) { - p++; - const uint8_t* start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (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 = 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; - } - - } // unnamed namespace +template +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)) { 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 INVALID_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 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; + 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); + // 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 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 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; + 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); + // 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 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; + 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); + // 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 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 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; + while (parse_digit(*src, i)) { src++; } + + // 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { + return (*src == '-'); +} + +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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 get_number_type(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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("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("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 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +} // unnamed namespace #endif // SIMDJSON_SKIPNUMBERPARSING - } // namespace numberparsing +} // 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; - } +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 westmere } // namespace simdjson #endif // SIMDJSON_GENERIC_NUMBERPARSING_H @@ -25590,84 +29843,113 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { +namespace westmere { - // - // internal::implementation_simdjson_result_base inline implementation - // +// +// internal::implementation_simdjson_result_base inline implementation +// - template - simdjson_inline void implementation_simdjson_result_base::tie(T& value, error_code& error) && noexcept { - error = this->second; - if (!error) { - value = std::forward>(*this).first; - } - } +template +simdjson_inline void implementation_simdjson_result_base::tie(T &value, error_code &error) && noexcept { + error = this->second; + if (!error) { + value = std::forward>(*this).first; + } +} - template - simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T& value) && noexcept { - error_code error; - std::forward>(*this).tie(value, error); - return error; - } +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T &value) && noexcept { + error_code error; + std::forward>(*this).tie(value, error); + return error; +} - template - simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { - return this->second; - } +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { + return this->second; +} + + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} #if SIMDJSON_EXCEPTIONS - template - simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return this->first; - } +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} - template - simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { - return std::forward>(*this).take_value(); - } +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} - template - simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return std::forward(this->first); - } +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} - template - simdjson_inline implementation_simdjson_result_base::operator T && () && noexcept(false) { - return std::forward>(*this).take_value(); - } + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + +template +simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return this->first; +} + +template +simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { + return std::forward>(*this).take_value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return std::forward(this->first); +} + +template +simdjson_inline implementation_simdjson_result_base::operator T&&() && noexcept(false) { + return std::forward>(*this).take_value(); +} #endif // SIMDJSON_EXCEPTIONS - template - simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { - return this->first; - } +template +simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { + return this->first; +} - template - simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { - return this->first; - } +template +simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { + return this->first; +} - template - simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { - return std::forward(this->first); - } +template +simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { + return std::forward(this->first); +} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T&& value, error_code error) noexcept - : first{ std::forward(value) }, second{ error } {} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept - : implementation_simdjson_result_base(T{}, error) {} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T&& value) noexcept - : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value, error_code error) noexcept + : first{std::forward(value)}, second{error} {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept + : implementation_simdjson_result_base(T{}, error) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value) noexcept + : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} - } // namespace westmere +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H @@ -25689,6 +29971,2642 @@ SIMDJSON_UNTARGET_REGION #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 // 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 struct simd8; +template 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 +#include + +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); +} + +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t *result) { + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); +} + +} // 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 + +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 + 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(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; + + // Forward-declared so they can be used by splat and friends. + template + struct simd8; + + template> + struct base8: base> { + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m256i _value) : base>(_value) {} + + friend simdjson_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return __lasx_xvseq_b(lhs, rhs); } + + static const int SIZE = sizeof(base>::value); + + template + simdjson_inline simd8 prev(const simd8 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: base8 { + static simdjson_inline simd8 splat(bool _value) { return __lasx_xvreplgr2vr_b(uint8_t(-(!!_value))); } + + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m256i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(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 operator~() const { return *this ^ true; } + }; + + template + struct base8_numeric: base8 { + static simdjson_inline simd8 splat(T _value) { + return __lasx_xvreplgr2vr_b(_value); + } + static simdjson_inline simd8 zero() { return __lasx_xvldi(0); } + static simdjson_inline simd8 load(const T values[32]) { + return __lasx_xvld(reinterpret_cast(values), 0); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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( + 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() {} + simdjson_inline base8_numeric(const __m256i _value) : base8(_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 operator+(const simd8 other) const { return __lasx_xvadd_b(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return __lasx_xvsub_b(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } + + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } + + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 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 + 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(reinterpret_cast(pshufb_combine_table)), pop1 * 8); + __m256i maskhi = __lasx_xvldx(reinterpret_cast(reinterpret_cast(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(output), 0); + uint64_t value3 = __lasx_xvpickve2gr_du(answer, 2); + uint64_t value4 = __lasx_xvpickve2gr_du(answer, 3); + uint64_t *pos = reinterpret_cast(reinterpret_cast(output) + 16 - (pop1 + pop2) / 2); + pos[0] = value3; + pos[1] = value4; + } + + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; + + // Signed bytes + template<> + struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( + 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 max_val(const simd8 other) const { return __lasx_xvmax_b(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return __lasx_xvmin_b(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return __lasx_xvslt_b(other, *this); } + simdjson_inline simd8 operator<(const simd8 other) const { return __lasx_xvslt_b(*this, other); } + }; + + // Unsigned bytes + template<> + struct simd8: base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( + 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 saturating_add(const simd8 other) const { return __lasx_xvsadd_bu(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return __lasx_xvssub_bu(*this, other); } + + // Order-specific operations + simdjson_inline simd8 max_val(const simd8 other) const { return __lasx_xvmax_bu(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return __lasx_xvmin_bu(other, *this); } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } + simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } + simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } + simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } + + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } + simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } + simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } + simdjson_inline simd8 any_bits_set(simd8 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 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 bits) const { return !bits_not_set_anywhere(bits); } + template + simdjson_inline simd8 shr() const { return simd8(__lasx_xvsrli_b(*this, N)); } + template + simdjson_inline simd8 shl() const { return simd8(__lasx_xvslli_b(*this, N)); } + }; + + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 2, "LASX kernel should use two registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } + + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed + + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1) : chunks{chunk0, chunk1} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::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)*0); + this->chunks[1].store(ptr+sizeof(simd8)*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 reduce_or() const { + return this->chunks[0] | this->chunks[1]; + } + + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] == mask, + this->chunks[1] == mask + ).to_bitmask(); + } + + simdjson_inline uint64_t eq(const simd8x64 &other) const { + return simd8x64( + 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 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] <= mask, + this->chunks[1] <= mask + ).to_bitmask(); + } + }; // struct simd8x64 + +} // 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 v(src); + v.store(dst); + return { + static_cast((v == '\\').to_bitmask()), // bs_bits + static_cast((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 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + static_cast((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 + +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; } + + +// 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); +} + +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; } +} + +} // 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_containers{}; + /** Whether each open container is a [ or { */ + std::unique_ptr 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) { return CAPACITY; } + // Stage 1 index output + size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; + 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 { + // 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 : public internal::implementation_simdjson_result_base { + * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} + * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} + * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} + * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} + * // Your extra methods here + * } + * + * Then any method returning simdjson_result will be chainable with your methods. + */ +template +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/internal/numberparsing_tables.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +#include +#include + +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(negative)) << 63); + std::memcpy(&d, &mantissa, sizeof(d)); + return d; +} + +// 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; +} + +// 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(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::max)() || *outDouble < std::numeric_limits::lowest()); +} + +static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) { + *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(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::max)() || *outDouble < std::numeric_limits::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); +} + +template +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(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(c - '0') <= 9; +} + +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; + +#ifdef SIMDJSON_SWAR_NUMBER_PARSING +#if SIMDJSON_SWAR_NUMBER_PARSING + // this helps if we have lots of decimals! + // this turns out to be frequent enough. + if (is_made_of_eight_digits_fast(p)) { + i = i * 100000000 + parse_eight_digits_unrolled(p); + p += 8; + } +#endif // SIMDJSON_SWAR_NUMBER_PARSING +#endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING + // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) + if (parse_digit(*p, i)) { ++p; } + while (parse_digit(*p, i)) { p++; } + 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 */ +static 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 +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 +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 +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 parse_unsigned(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t * src) 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 +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)) { 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 INVALID_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 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; + 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); + // 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 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 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; + 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); + // 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 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; + 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); + // 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 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 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; + while (parse_digit(*src, i)) { src++; } + + // 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { + return (*src == '-'); +} + +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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 get_number_type(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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("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("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 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +} // 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 inline implementation +// + +template +simdjson_inline void implementation_simdjson_result_base::tie(T &value, error_code &error) && noexcept { + error = this->second; + if (!error) { + value = std::forward>(*this).first; + } +} + +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T &value) && noexcept { + error_code error; + std::forward>(*this).tie(value, error); + return error; +} + +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { + return this->second; +} + + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} + +#if SIMDJSON_EXCEPTIONS + +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} + +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + +template +simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return this->first; +} + +template +simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { + return std::forward>(*this).take_value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return std::forward(this->first); +} + +template +simdjson_inline implementation_simdjson_result_base::operator T&&() && noexcept(false) { + return std::forward>(*this).take_value(); +} + +#endif // SIMDJSON_EXCEPTIONS + +template +simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { + return this->first; +} + +template +simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { + return this->first; +} + +template +simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { + return std::forward(this->first); +} + +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value, error_code error) noexcept + : first{std::forward(value)}, second{error} {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept + : implementation_simdjson_result_base(T{}, error) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value) noexcept + : implementation_simdjson_result_base(std::forward(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 */ @@ -25709,21 +32627,21 @@ SIMDJSON_UNTARGET_REGION /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - /** - * Implementation for LSX. - */ - namespace lsx { +/** + * Implementation for LSX. + */ +namespace lsx { - class implementation; +class implementation; - namespace { - namespace simd { - template struct simd8; - template struct simd8x64; - } // namespace simd - } // unnamed namespace +namespace { +namespace simd { +template struct simd8; +template struct simd8x64; +} // namespace simd +} // unnamed namespace - } // namespace lsx +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_LSX_BASE_H @@ -25737,8 +32655,6 @@ namespace simdjson { /* amalgamation skipped (editor-only): #include "simdjson/lsx/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 static_assert(sizeof(__m128i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for LoongArch SX"); @@ -25757,43 +32673,43 @@ static_assert(sizeof(__m128i) <= simdjson::SIMDJSON_PADDING, "insufficient paddi /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace { +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); - } +// 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 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 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); - } +/* 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); +} - simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t* result) { - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); - } +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t *result) { + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); +} - } // unnamed namespace - } // namespace lsx +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_LSX_BITMANIPULATION_H @@ -25808,26 +32724,26 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace { +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; - } +// +// 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 +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif @@ -25846,33 +32762,39 @@ namespace simdjson { #include namespace simdjson { - namespace lsx { - namespace numberparsing { +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); - } +// 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; - } +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 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 */ @@ -25888,347 +32810,348 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace { - namespace simd { +namespace lsx { +namespace { +namespace simd { - // Forward-declared so they can be used by splat and friends. - template - struct base { - __m128i value; + // Forward-declared so they can be used by splat and friends. + template + struct base { + __m128i value; - // Zero constructor - simdjson_inline base() : value{ __m128i() } {} + // Zero constructor + simdjson_inline base() : value{__m128i()} {} - // Conversion from SIMD register - simdjson_inline base(const __m128i _value) : value(_value) {} + // 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; } + // 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(this); *this_cast = *this_cast | other; return *this_cast; } - simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } - simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } - }; + // 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(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; - // Forward-declared so they can be used by splat and friends. - template - struct simd8; + // Forward-declared so they can be used by splat and friends. + template + struct simd8; - template> - struct base8 : base> { - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m128i _value) : base>(_value) {} + template> + struct base8: base> { + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m128i _value) : base>(_value) {} - friend simdjson_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return __lsx_vseq_b(lhs, rhs); } + friend simdjson_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return __lsx_vseq_b(lhs, rhs); } - static const int SIZE = sizeof(base>::value); + static const int SIZE = sizeof(base>::value); - template - simdjson_inline simd8 prev(const simd8 prev_chunk) const { - return __lsx_vor_v(__lsx_vbsll_v(*this, N), __lsx_vbsrl_v(prev_chunk, 16 - N)); - } - }; + template + simdjson_inline simd8 prev(const simd8 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 : base8 { - static simdjson_inline simd8 splat(bool _value) { - return __lsx_vreplgr2vr_b(uint8_t(-(!!_value))); - } + // SIMD byte mask type (returned by things like eq and gt) + template<> + struct simd8: base8 { + static simdjson_inline simd8 splat(bool _value) { + return __lsx_vreplgr2vr_b(uint8_t(-(!!_value))); + } - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m128i _value) : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) : base8(splat(_value)) {} + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m128i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(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 operator~() const { return *this ^ true; } - }; + 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 operator~() const { return *this ^ true; } + }; - template - struct base8_numeric : base8 { - static simdjson_inline simd8 splat(T _value) { return __lsx_vreplgr2vr_b(_value); } - static simdjson_inline simd8 zero() { return __lsx_vldi(0); } - static simdjson_inline simd8 load(const T values[16]) { - return __lsx_vld(reinterpret_cast(values), 0); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdjson_inline simd8 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + template + struct base8_numeric: base8 { + static simdjson_inline simd8 splat(T _value) { return __lsx_vreplgr2vr_b(_value); } + static simdjson_inline simd8 zero() { return __lsx_vldi(0); } + static simdjson_inline simd8 load(const T values[16]) { + return __lsx_vld(reinterpret_cast(values), 0); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - simdjson_inline base8_numeric() : base8() {} - simdjson_inline base8_numeric(const __m128i _value) : base8(_value) {} + simdjson_inline base8_numeric() : base8() {} + simdjson_inline base8_numeric(const __m128i _value) : base8(_value) {} - // Store to array - simdjson_inline void store(T dst[16]) const { - return __lsx_vst(*this, reinterpret_cast<__m128i*>(dst), 0); - } + // 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 operator+(const simd8 other) const { return __lsx_vadd_b(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return __lsx_vsub_b(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { return __lsx_vadd_b(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return __lsx_vsub_b(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } - // Override to distinguish from bool version - simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return __lsx_vshuf_b(lookup_table, lookup_table, *this); - } + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 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 - 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(reinterpret_cast(pshufb_combine_table)), pop1 * 8); - __m128i answer = __lsx_vshuf_b(pruned, pruned, compactmask); - __lsx_vst(answer, reinterpret_cast(output), 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. + template + 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(reinterpret_cast(pshufb_combine_table)), pop1 * 8); + __m128i answer = __lsx_vshuf_b(pruned, pruned, compactmask); + __lsx_vst(answer, reinterpret_cast(output), 0); + } - template - simdjson_inline simd8 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::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - }; + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; - // Signed bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + // Signed bytes + template<> + struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - // Order-sensitive comparisons - simdjson_inline simd8 max_val(const simd8 other) const { return __lsx_vmax_b(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return __lsx_vmin_b(*this, other); } - simdjson_inline simd8 operator>(const simd8 other) const { return __lsx_vslt_b(other, *this); } - simdjson_inline simd8 operator<(const simd8 other) const { return __lsx_vslt_b(*this, other); } - }; + // Order-sensitive comparisons + simdjson_inline simd8 max_val(const simd8 other) const { return __lsx_vmax_b(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return __lsx_vmin_b(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return __lsx_vslt_b(other, *this); } + simdjson_inline simd8 operator<(const simd8 other) const { return __lsx_vslt_b(*this, other); } + }; - // Unsigned bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + // Unsigned bytes + template<> + struct simd8: base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - // Saturated math - simdjson_inline simd8 saturating_add(const simd8 other) const { return __lsx_vsadd_bu(*this, other); } - simdjson_inline simd8 saturating_sub(const simd8 other) const { return __lsx_vssub_bu(*this, other); } + // Saturated math + simdjson_inline simd8 saturating_add(const simd8 other) const { return __lsx_vsadd_bu(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return __lsx_vssub_bu(*this, other); } - // Order-specific operations - simdjson_inline simd8 max_val(const simd8 other) const { return __lsx_vmax_bu(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return __lsx_vmin_bu(other, *this); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } - simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } - simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } - simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } + // Order-specific operations + simdjson_inline simd8 max_val(const simd8 other) const { return __lsx_vmax_bu(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return __lsx_vmin_bu(other, *this); } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } + simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } + simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } + simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } - // Bit-specific operations - simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } - simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } - simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } - simdjson_inline simd8 any_bits_set(simd8 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 bits) const { - return 0 == __lsx_vpickve2gr_hu(__lsx_vmsknz_b(__lsx_vand_v(*this, bits)), 0); - } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } - template - simdjson_inline simd8 shr() const { return simd8(__lsx_vsrli_b(*this, N)); } - template - simdjson_inline simd8 shl() const { return simd8(__lsx_vslli_b(*this, N)); } - }; + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } + simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } + simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } + simdjson_inline simd8 any_bits_set(simd8 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 bits) const { + return 0 == __lsx_vpickve2gr_hu(__lsx_vmsknz_b(__lsx_vand_v(*this, bits)), 0); + } + simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } + template + simdjson_inline simd8 shr() const { return simd8(__lsx_vsrli_b(*this, N)); } + template + simdjson_inline simd8 shl() const { return simd8(__lsx_vslli_b(*this, N)); } + }; - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 4, "LSX kernel should use four registers per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 4, "LSX kernel should use four registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{ chunk0, chunk1, chunk2, chunk3 } {} - simdjson_inline simd8x64(const T ptr[64]) : chunks{ simd8::load(ptr), simd8::load(ptr + 16), simd8::load(ptr + 32), simd8::load(ptr + 48) } {} + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::load(ptr+16), simd8::load(ptr+32), simd8::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(output); - // There should be a critical value which processes in scaler is faster. - if (zcnt1) - this->chunks[0].compress(mask1, reinterpret_cast(voutput)); - voutput += zcnt1; - if (zcnt2) - this->chunks[1].compress(mask2, reinterpret_cast(voutput)); - voutput += zcnt2; - if (zcnt3) - this->chunks[2].compress(mask3, reinterpret_cast(voutput)); - voutput += zcnt3; - if (zcnt4) - this->chunks[3].compress(mask4, reinterpret_cast(voutput)); - voutput += zcnt4; - return reinterpret_cast(voutput) - reinterpret_cast(output); - } + 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(output); + // There should be a critical value which processes in scaler is faster. + if (zcnt1) + this->chunks[0].compress(mask1, reinterpret_cast(voutput)); + voutput += zcnt1; + if (zcnt2) + this->chunks[1].compress(mask2, reinterpret_cast(voutput)); + voutput += zcnt2; + if (zcnt3) + this->chunks[2].compress(mask3, reinterpret_cast(voutput)); + voutput += zcnt3; + if (zcnt4) + this->chunks[3].compress(mask4, reinterpret_cast(voutput)); + voutput += zcnt4; + return reinterpret_cast(voutput) - reinterpret_cast(output); + } - simdjson_inline void store(T ptr[64]) const { - this->chunks[0].store(ptr + sizeof(simd8) * 0); - this->chunks[1].store(ptr + sizeof(simd8) * 1); - this->chunks[2].store(ptr + sizeof(simd8) * 2); - this->chunks[3].store(ptr + sizeof(simd8) * 3); - } + simdjson_inline void store(T ptr[64]) const { + this->chunks[0].store(ptr+sizeof(simd8)*0); + this->chunks[1].store(ptr+sizeof(simd8)*1); + this->chunks[2].store(ptr+sizeof(simd8)*2); + this->chunks[3].store(ptr+sizeof(simd8)*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 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 reduce_or() const { - return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]); - } + simdjson_inline simd8 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] == mask, - this->chunks[1] == mask, - this->chunks[2] == mask, - this->chunks[3] == mask - ).to_bitmask(); - } + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + 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& other) const { - return simd8x64( - 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 eq(const simd8x64 &other) const { + return simd8x64( + 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] <= mask, - this->chunks[1] <= mask, - this->chunks[2] <= mask, - this->chunks[3] <= mask - ).to_bitmask(); - } - }; // struct simd8x64 + simdjson_inline uint64_t lteq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] <= mask, + this->chunks[1] <= mask, + this->chunks[2] <= mask, + this->chunks[3] <= mask + ).to_bitmask(); + } + }; // struct simd8x64 - } // namespace simd - } // unnamed namespace - } // namespace lsx +} // namespace simd +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_LSX_SIMD_H @@ -26245,46 +33168,71 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace { +namespace lsx { +namespace { - using namespace simd; +using namespace simd; - // Holds backslashes and quotes locations. - struct backslash_and_quote { - public: - static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t* src, uint8_t* dst); +// 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); } + 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 + 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 v0(src); - simd8 v1(src + sizeof(v0)); - v0.store(dst); - v1.store(dst + sizeof(v0)); +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 v0(src); + simd8 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(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); - return { - uint32_t(bs_and_quote), // bs_bits - uint32_t(bs_and_quote >> 32) // quote_bits - }; - } + // 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(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); + return { + uint32_t(bs_and_quote), // bs_bits + uint32_t(bs_and_quote >> 32) // quote_bits + }; +} - } // unnamed namespace - } // namespace lsx + +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 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + static_cast((is_backslash | is_quote | is_control).to_bitmask()) + }; +} + +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_LSX_STRINGPARSING_DEFS_H @@ -26319,10 +33267,12 @@ namespace simdjson { /* 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_LSX */ -/* amalgamation skipped (editor-only): #include "simdjson/lsx/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 */ @@ -26332,22 +33282,22 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { +namespace lsx { - struct open_container; - class dom_parser_implementation; +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 - }; +/** + * 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 lsx } // namespace simdjson #endif // SIMDJSON_GENERIC_BASE_H @@ -26364,99 +33314,97 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace { - namespace jsoncharutils { +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]; - } +// 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]; - } +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; - } +// 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 - } +// 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; - } +// 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 jsoncharutils +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_GENERIC_JSONCHARUTILS_H @@ -26474,69 +33422,69 @@ namespace simdjson { #include namespace simdjson { - namespace lsx { - namespace { - /// @private - namespace atomparsing { +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; } +// 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; } - // 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); - } +// 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); +} - 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) { + 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_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) { + 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_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) { + 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; } - } +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; } +} - } // namespace atomparsing - } // unnamed namespace - } // namespace lsx +} // namespace atomparsing +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_GENERIC_ATOMPARSING_H @@ -26552,83 +33500,83 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { +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 +// 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"); +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_containers{}; - /** Whether each open container is a [ or { */ - std::unique_ptr 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{}; +class dom_parser_implementation final : public internal::dom_parser_implementation { +public: + /** Tape location of each open { or [ */ + std::unique_ptr open_containers{}; + /** Whether each open container is a [ or { */ + std::unique_ptr 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; + 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); + 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 lsx } // namespace simdjson namespace simdjson { - namespace lsx { +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; +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) { return CAPACITY; } - // Stage 1 index output - size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; - 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; +// 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) { return CAPACITY; } + // Stage 1 index output + size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; + 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; - } + _capacity = capacity; + return SUCCESS; +} - inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { - // 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; } +inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { + // 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; - } + _max_depth = max_depth; + return SUCCESS; +} - } // namespace lsx +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H @@ -26643,129 +33591,148 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { +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 : public internal::implementation_simdjson_result_base { - * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} - * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} - * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} - * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} - * // Your extra methods here - * } - * - * Then any method returning simdjson_result will be chainable with your methods. - */ - template - struct implementation_simdjson_result_base { +// 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 : public internal::implementation_simdjson_result_base { + * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} + * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} + * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} + * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} + * // Your extra methods here + * } + * + * Then any method returning simdjson_result will be chainable with your methods. + */ +template +struct implementation_simdjson_result_base { - /** - * Create a new empty result with error = UNINITIALIZED. - */ - simdjson_inline implementation_simdjson_result_base() noexcept = default; + /** + * 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 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 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; + /** + * 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 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; + /** + * 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_inline error_code error() const 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& value() & noexcept(false); + /** + * 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); - /** - * Take the result value (move it). - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline T&& value() && 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&& take_value() && noexcept(false); + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& 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); + /** + * 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; - 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 + /** + * 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; - } // namespace lsx + 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 @@ -26786,8 +33753,8 @@ namespace simdjson { #include namespace simdjson { - namespace lsx { - namespace numberparsing { +namespace lsx { +namespace numberparsing { #ifdef JSON_TEST_NUMBERS #define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) @@ -26803,540 +33770,547 @@ namespace simdjson { #define BIGINT_NUMBER(SRC) (BIGINT_ERROR) #endif - namespace { +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(negative)) << 63); - std::memcpy(&d, &mantissa, sizeof(d)); - return d; - } +// 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(negative)) << 63); + std::memcpy(&d, &mantissa, sizeof(d)); + return d; +} - // 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 +// 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) + if (0 <= power && power <= 22 && i <= 9007199254740991) #else - if (-22 <= power && power <= 22 && i <= 9007199254740991) + 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 + { + // 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. + // 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; - } + // 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 - // approximatively 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; + // 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 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. - simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]); - // 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. + // 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 - // 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. - simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); - 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); + // 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. - // 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 - } - } + // 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); - mantissa += mantissa & 1; - mantissa >>= 1; + // 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 + } + } - // 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; - } + mantissa += mantissa & 1; + mantissa >>= 1; - // 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(ptr)); - // We do not accept infinite values. + // 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; +} - // 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::max)() || *outDouble < std::numeric_limits::lowest()); - } +// 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(ptr)); + // We do not accept infinite values. - static bool parse_float_fallback(const uint8_t* ptr, const uint8_t* end_ptr, double* outDouble) { - *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(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::max)() || *outDouble < std::numeric_limits::lowest()); +} - // 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::max)() || *outDouble < std::numeric_limits::lowest()); - } +static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) { + *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(end_ptr)); + // We do not accept infinite values. - // 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); - } + // 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::max)() || *outDouble < std::numeric_limits::lowest()); +} - template - 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(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; - } +// 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); +} - simdjson_inline bool is_digit(const uint8_t c) { - return static_cast(c - '0') <= 9; - } +template +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(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 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; +simdjson_inline bool is_digit(const uint8_t c) { + return static_cast(c - '0') <= 9; +} + +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; #ifdef SIMDJSON_SWAR_NUMBER_PARSING #if SIMDJSON_SWAR_NUMBER_PARSING - // this helps if we have lots of decimals! - // this turns out to be frequent enough. - if (is_made_of_eight_digits_fast(p)) { - i = i * 100000000 + parse_eight_digits_unrolled(p); - p += 8; - } + // this helps if we have lots of decimals! + // this turns out to be frequent enough. + if (is_made_of_eight_digits_fast(p)) { + i = i * 100000000 + parse_eight_digits_unrolled(p); + p += 8; + } #endif // SIMDJSON_SWAR_NUMBER_PARSING #endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING - // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) - if (parse_digit(*p, i)) { ++p; } - while (parse_digit(*p, i)) { p++; } - exponent = first_after_period - p; - // Decimal without digits (123.) is illegal - if (exponent == 0) { - return INVALID_NUMBER(src); - } - return SUCCESS; - } + // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) + if (parse_digit(*p, i)) { ++p; } + while (parse_digit(*p, i)) { p++; } + exponent = first_after_period - p; + // Decimal without digits (123.) is illegal + if (exponent == 0) { + return INVALID_NUMBER(src); + } + return SUCCESS; +} - 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 +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. + // 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. + // 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 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; - } + // 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 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); - } +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 +} // unnamed namespace - /** @private */ - static 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 */ +static 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 - 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; - } +/** @private */ +template +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 - simdjson_inline error_code parse_number(const uint8_t* const src, W& writer); +// 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 +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 +// for performance analysis, it is sometimes useful to skip parsing #ifdef SIMDJSON_SKIPNUMBERPARSING - template - simdjson_inline error_code parse_number(const uint8_t* const, W& writer) { - writer.append_s64(0); // always write zero - return SUCCESS; // always succeeds - } +template +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 parse_unsigned(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline bool is_negative(const uint8_t* src) noexcept { return false; } - simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t* src) noexcept { return false; } - simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t* src) noexcept { return number_type::signed_integer; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t * src) noexcept { return number_type::signed_integer; } #else // parse the number at src @@ -27348,744 +34322,753 @@ namespace simdjson { // content and append a space before calling this function. // // Our objective is accurate parsing (ULP of 0) at high speed. - template - 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)) { 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 INVALID_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 { - WRITE_INTEGER(negative ? (~i + 1) : i, src, writer); - } - 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 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; - 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); - // 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 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 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; - 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); - // 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 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; - 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); - // 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 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 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; - while (parse_digit(*src, i)) { src++; } - - // 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 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; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (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 == '.')) { - p++; - const uint8_t* start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (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 = 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; - } - - simdjson_unused simdjson_inline bool is_negative(const uint8_t* src) noexcept { - return (*src == '-'); - } - - simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t* src) noexcept { - bool negative = (*src == '-'); - src += uint8_t(negative); - const uint8_t* p = src; - while (static_cast(*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 get_number_type(const uint8_t* src) noexcept { - bool negative = (*src == '-'); - src += uint8_t(negative); - const uint8_t* p = src; - while (static_cast(*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("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; - } - 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("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 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 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; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (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 == '.')) { - p++; - const uint8_t* start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (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 = 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; - } - - } // unnamed namespace +template +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)) { 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 INVALID_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 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; + 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); + // 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 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 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; + 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); + // 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 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; + 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); + // 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 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 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; + while (parse_digit(*src, i)) { src++; } + + // 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { + return (*src == '-'); +} + +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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 get_number_type(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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("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("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 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +} // unnamed namespace #endif // SIMDJSON_SKIPNUMBERPARSING - } // namespace numberparsing +} // 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; - } +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 lsx } // namespace simdjson #endif // SIMDJSON_GENERIC_NUMBERPARSING_H @@ -28102,84 +35085,113 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { +namespace lsx { - // - // internal::implementation_simdjson_result_base inline implementation - // +// +// internal::implementation_simdjson_result_base inline implementation +// - template - simdjson_inline void implementation_simdjson_result_base::tie(T& value, error_code& error) && noexcept { - error = this->second; - if (!error) { - value = std::forward>(*this).first; - } - } +template +simdjson_inline void implementation_simdjson_result_base::tie(T &value, error_code &error) && noexcept { + error = this->second; + if (!error) { + value = std::forward>(*this).first; + } +} - template - simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T& value) && noexcept { - error_code error; - std::forward>(*this).tie(value, error); - return error; - } +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T &value) && noexcept { + error_code error; + std::forward>(*this).tie(value, error); + return error; +} - template - simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { - return this->second; - } +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { + return this->second; +} + + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} #if SIMDJSON_EXCEPTIONS - template - simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return this->first; - } +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} - template - simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { - return std::forward>(*this).take_value(); - } +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} - template - simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return std::forward(this->first); - } +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} - template - simdjson_inline implementation_simdjson_result_base::operator T && () && noexcept(false) { - return std::forward>(*this).take_value(); - } + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + +template +simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return this->first; +} + +template +simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { + return std::forward>(*this).take_value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return std::forward(this->first); +} + +template +simdjson_inline implementation_simdjson_result_base::operator T&&() && noexcept(false) { + return std::forward>(*this).take_value(); +} #endif // SIMDJSON_EXCEPTIONS - template - simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { - return this->first; - } +template +simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { + return this->first; +} - template - simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { - return this->first; - } +template +simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { + return this->first; +} - template - simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { - return std::forward(this->first); - } +template +simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { + return std::forward(this->first); +} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T&& value, error_code error) noexcept - : first{ std::forward(value) }, second{ error } {} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept - : implementation_simdjson_result_base(T{}, error) {} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T&& value) noexcept - : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value, error_code error) noexcept + : first{std::forward(value)}, second{error} {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept + : implementation_simdjson_result_base(T{}, error) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value) noexcept + : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} - } // namespace lsx +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H @@ -28198,633 +35210,671 @@ namespace simdjson { #endif // SIMDJSON_LSX_H /* end file simdjson/lsx.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 +#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/lasx/begin.h: #include "simdjson/lasx/begin.h" */ -/* begin file simdjson/lasx/begin.h */ -/* defining SIMDJSON_IMPLEMENTATION to "lasx" */ -#define SIMDJSON_IMPLEMENTATION lasx -/* 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 + +/* 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 { - /** - * Implementation for LASX. - */ - namespace lasx { +/** + * RVV-VLS implementation. + */ +namespace rvv_vls { - class implementation; +class implementation; - namespace { - namespace simd { - template struct simd8; - template struct simd8x64; - } // namespace simd - } // unnamed namespace - - } // namespace lasx +} // namespace rvv_vls } // 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 +#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/lasx/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/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 +#include -static_assert(sizeof(__m256i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for LoongArch ASX"); +#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())) -#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); - } - - simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t* result) { - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); - } - - } // 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 +#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 #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 + +#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/lasx/base.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/lasx/intrinsics.h" */ +/* 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); +} + +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); +} + +} // 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 + struct simd8; + + // SIMD byte mask type (returned by things like eq and gt) + template<> + struct simd8 { + 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 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 operator|(const simd8 other) const { return __riscv_vmor(*this, other, VL8); } + simdjson_inline simd8 operator&(const simd8 other) const { return __riscv_vmand(*this, other, VL8); } + simdjson_inline simd8 operator^(const simd8 other) const { return __riscv_vmxor(*this, other, VL8); } + simdjson_inline simd8 bit_andnot(const simd8 other) const { return __riscv_vmandn(other, *this, VL8); } + simdjson_inline simd8 operator~() const { return __riscv_vmnot(*this, VL8); } + simdjson_inline simd8& operator|=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline simd8& operator&=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline simd8& operator^=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; + + // Unsigned bytes + template<> + struct simd8 { + + 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 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 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( + 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 operator|(const simd8 other) const { return __riscv_vor_vv_u8m1( value, other, VL8); } + simdjson_inline simd8 operator&(const simd8 other) const { return __riscv_vand_vv_u8m1( value, other, VL8); } + simdjson_inline simd8 operator^(const simd8 other) const { return __riscv_vxor_vv_u8m1( value, other, VL8); } + simdjson_inline simd8 operator~() const { return __riscv_vnot_v_u8m1(value, VL8); } +#if __riscv_zvbb + simdjson_inline simd8 bit_andnot(const simd8 other) const { return __riscv_vandn_vv_u8m1(other, value, VL8); } +#else + simdjson_inline simd8 bit_andnot(const simd8 other) const { return other & ~*this; } +#endif + simdjson_inline simd8& operator|=(const simd8 other) { value = *this | other; return *this; } + simdjson_inline simd8& operator&=(const simd8 other) { value = *this & other; return *this; } + simdjson_inline simd8& operator^=(const simd8 other) { value = *this ^ other; return *this; } + + simdjson_inline simd8 operator==(const simd8 other) const { return __riscv_vmseq(value, other, VL8); } + simdjson_inline simd8 operator==(uint8_t other) const { return __riscv_vmseq(value, other, VL8); } + + template + simdjson_inline simd8 prev(const simd8 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 saturating_add(const simd8 other) const { return __riscv_vsaddu(value, other, VL8); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return __riscv_vssubu(value, other, VL8); } + + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { return __riscv_vadd(value, other, VL8); } + simdjson_inline simd8 operator-(const simd8 other) const { return __riscv_vsub(value, other, VL8); } + simdjson_inline simd8& operator+=(const simd8 other) { value = *this + other; return *this; } + simdjson_inline simd8& operator-=(const simd8 other) { value = *this - other; return *this; } + + // Order-specific operations + simdjson_inline simd8 operator<=(const simd8 other) const { return __riscv_vmsleu(value, other, VL8); } + simdjson_inline simd8 operator>=(const simd8 other) const { return __riscv_vmsgeu(value, other, VL8); } + simdjson_inline simd8 operator<(const simd8 other) const { return __riscv_vmsltu(value, other, VL8); } + simdjson_inline simd8 operator>(const simd8 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 gt_bits(const simd8 other) const { return simd8(*this > other); } + // Same as <, but instead of guaranteeing all 1's == true, false = 0 and true = nonzero. + simdjson_inline simd8 lt_bits(const simd8 other) const { return simd8(*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 bits) const { return (*this & bits).any_bits_set_anywhere(); } + template + simdjson_inline simd8 shr() const { return __riscv_vsrl(value, N, VL8); } + template + simdjson_inline simd8 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 + simdjson_inline simd8 lookup_16(simd8 lookup_table) const { + return __riscv_vrgather(lookup_table, value, VL8); + } + + // compress inactive elements, to match AVX-512 behavior + template + 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 + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; + + // Signed bytes + template<> + struct simd8 { + 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 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( + 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() const { return __riscv_vreinterpret_u8m1(value); } + + // Math + simdjson_inline simd8 operator+(const simd8 other) const { return __riscv_vadd(value, other, VL8); } + simdjson_inline simd8 operator-(const simd8 other) const { return __riscv_vsub(value, other, VL8); } + simdjson_inline simd8& operator+=(const simd8 other) { value = *this + other; return *this; } + simdjson_inline simd8& operator-=(const simd8 other) { value = *this - other; return *this; } + + // Order-sensitive comparisons + simdjson_inline simd8 max_val( const simd8 other) const { return __riscv_vmax( value, other, VL8); } + simdjson_inline simd8 min_val( const simd8 other) const { return __riscv_vmin( value, other, VL8); } + simdjson_inline simd8 operator>( const simd8 other) const { return __riscv_vmsgt(value, other, VL8); } + simdjson_inline simd8 operator<( const simd8 other) const { return __riscv_vmslt(value, other, VL8); } + simdjson_inline simd8 operator==(const simd8 other) const { return __riscv_vmseq(value, other, VL8); } + + template + simdjson_inline simd8 prev(const simd8 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 + simdjson_inline simd8 lookup_16(simd8 lookup_table) const { + return __riscv_vrgather(lookup_table, value, VL8); + } + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; + + template + struct simd8x64; + template<> + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + vuint8x64_t value; + +#if __riscv_v_fixed_vlen >= 512 + template simd8 get() const { return value; } +#else + template simd8 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& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& 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 + +} // 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); + 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 v(src); + v.store(dst); + return { (v == '\\').to_bitmask(), (v == '"').to_bitmask() }; +} + +struct escaping { + static constexpr uint64_t BYTES_PROCESSED = sizeof(simd8); + 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 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 +#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 lasx { - namespace numberparsing { +namespace rvv_vls { +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); - } +// 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); +} - 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; - } +/** @private */ +static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) { + return parse_eight_digits_unrolled(reinterpret_cast(chars)); +} - } // namespace numberparsing - } // namespace lasx +/** @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_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 - 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(this); *this_cast = *this_cast | other; return *this_cast; } - simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } - simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } - }; - - // Forward-declared so they can be used by splat and friends. - template - struct simd8; - - template> - struct base8 : base> { - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m256i _value) : base>(_value) {} - - friend simdjson_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return __lasx_xvseq_b(lhs, rhs); } - - static const int SIZE = sizeof(base>::value); - - template - simdjson_inline simd8 prev(const simd8 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 : base8 { - static simdjson_inline simd8 splat(bool _value) { return __lasx_xvreplgr2vr_b(uint8_t(-(!!_value))); } - - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m256i _value) : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) : base8(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 operator~() const { return *this ^ true; } - }; - - template - struct base8_numeric : base8 { - static simdjson_inline simd8 splat(T _value) { - return __lasx_xvreplgr2vr_b(_value); - } - static simdjson_inline simd8 zero() { return __lasx_xvldi(0); } - static simdjson_inline simd8 load(const T values[32]) { - return __lasx_xvld(reinterpret_cast(values), 0); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdjson_inline simd8 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( - 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() {} - simdjson_inline base8_numeric(const __m256i _value) : base8(_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 operator+(const simd8 other) const { return __lasx_xvadd_b(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return __lasx_xvsub_b(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } - - // Override to distinguish from bool version - simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 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 - 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(reinterpret_cast(pshufb_combine_table)), pop1 * 8); - __m256i maskhi = __lasx_xvldx(reinterpret_cast(reinterpret_cast(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(output), 0); - uint64_t value3 = __lasx_xvpickve2gr_du(answer, 2); - uint64_t value4 = __lasx_xvpickve2gr_du(answer, 3); - uint64_t* pos = reinterpret_cast(reinterpret_cast(output) + 16 - (pop1 + pop2) / 2); - pos[0] = value3; - pos[1] = value4; - } - - template - simdjson_inline simd8 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::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - }; - - // Signed bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( - 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 max_val(const simd8 other) const { return __lasx_xvmax_b(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return __lasx_xvmin_b(*this, other); } - simdjson_inline simd8 operator>(const simd8 other) const { return __lasx_xvslt_b(other, *this); } - simdjson_inline simd8 operator<(const simd8 other) const { return __lasx_xvslt_b(*this, other); } - }; - - // Unsigned bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( - 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 saturating_add(const simd8 other) const { return __lasx_xvsadd_bu(*this, other); } - simdjson_inline simd8 saturating_sub(const simd8 other) const { return __lasx_xvssub_bu(*this, other); } - - // Order-specific operations - simdjson_inline simd8 max_val(const simd8 other) const { return __lasx_xvmax_bu(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return __lasx_xvmin_bu(other, *this); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } - simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } - simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } - simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } - - // Bit-specific operations - simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } - simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } - simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } - simdjson_inline simd8 any_bits_set(simd8 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 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 bits) const { return !bits_not_set_anywhere(bits); } - template - simdjson_inline simd8 shr() const { return simd8(__lasx_xvsrli_b(*this, N)); } - template - simdjson_inline simd8 shl() const { return simd8(__lasx_xvslli_b(*this, N)); } - }; - - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 2, "LASX kernel should use two registers per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; - - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed - - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1) : chunks{ chunk0, chunk1 } {} - simdjson_inline simd8x64(const T ptr[64]) : chunks{ simd8::load(ptr), simd8::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) * 0); - this->chunks[1].store(ptr + sizeof(simd8) * 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 reduce_or() const { - return this->chunks[0] | this->chunks[1]; - } - - simdjson_inline uint64_t eq(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] == mask, - this->chunks[1] == mask - ).to_bitmask(); - } - - simdjson_inline uint64_t eq(const simd8x64& other) const { - return simd8x64( - 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] <= mask, - this->chunks[1] <= mask - ).to_bitmask(); - } - }; // struct simd8x64 - - } // 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 static 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 v(src); - v.store(dst); - return { - static_cast((v == '\\').to_bitmask()), // bs_bits - static_cast((v == '"').to_bitmask()), // quote_bits - }; - } - - } // unnamed namespace - } // namespace lasx -} // namespace simdjson - -#endif // SIMDJSON_LASX_STRINGPARSING_DEFS_H -/* end file simdjson/lasx/stringparsing_defs.h */ +#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/lasx/begin.h */ -/* including simdjson/generic/amalgamated.h for lasx: #include "simdjson/generic/amalgamated.h" */ -/* begin file simdjson/generic/amalgamated.h for lasx */ +/* 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 lasx: #include "simdjson/generic/base.h" */ -/* begin file simdjson/generic/base.h for lasx */ +/* 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 */ @@ -28844,10 +35894,12 @@ namespace simdjson { /* 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_LSX */ -/* amalgamation skipped (editor-only): #include "simdjson/lsx/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 */ @@ -28857,28 +35909,28 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { +namespace rvv_vls { - struct open_container; - class dom_parser_implementation; +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 - }; +/** + * 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 rvv_vls } // 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 */ +/* 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 */ @@ -28889,105 +35941,103 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace { - namespace jsoncharutils { +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]; - } +// 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]; - } +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; - } +// 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 - } +// 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; - } +// 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 jsoncharutils +} // unnamed namespace +} // namespace rvv_vls } // 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 */ +/* 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 */ @@ -28999,75 +36049,75 @@ namespace simdjson { #include namespace simdjson { - namespace lasx { - namespace { - /// @private - namespace atomparsing { +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; } +// 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; } - // 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); - } +// 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); +} - 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) { + 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_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) { + 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_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) { + 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; } - } +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; } +} - } // namespace atomparsing - } // unnamed namespace - } // namespace lasx +} // namespace atomparsing +} // unnamed namespace +} // namespace rvv_vls } // 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 */ +/* 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 */ @@ -29077,89 +36127,89 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { +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 +// 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"); +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_containers{}; - /** Whether each open container is a [ or { */ - std::unique_ptr 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{}; +class dom_parser_implementation final : public internal::dom_parser_implementation { +public: + /** Tape location of each open { or [ */ + std::unique_ptr open_containers{}; + /** Whether each open container is a [ or { */ + std::unique_ptr 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; + 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); + 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 rvv_vls } // namespace simdjson namespace simdjson { - namespace lasx { +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; +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) { return CAPACITY; } - // Stage 1 index output - size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; - 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; +// 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) { return CAPACITY; } + // Stage 1 index output + size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; + 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; - } + _capacity = capacity; + return SUCCESS; +} - inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { - // 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; } +inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { + // 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; - } + _max_depth = max_depth; + return SUCCESS; +} - } // namespace lasx +} // namespace rvv_vls } // 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 */ +/* 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 */ @@ -29168,135 +36218,154 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { +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 : public internal::implementation_simdjson_result_base { - * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} - * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} - * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} - * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} - * // Your extra methods here - * } - * - * Then any method returning simdjson_result will be chainable with your methods. - */ - template - struct implementation_simdjson_result_base { +// 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 : public internal::implementation_simdjson_result_base { + * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} + * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} + * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} + * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} + * // Your extra methods here + * } + * + * Then any method returning simdjson_result will be chainable with your methods. + */ +template +struct implementation_simdjson_result_base { - /** - * Create a new empty result with error = UNINITIALIZED. - */ - simdjson_inline implementation_simdjson_result_base() noexcept = default; + /** + * 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 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 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; + /** + * 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 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; + /** + * 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_inline error_code error() const 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& value() & noexcept(false); + /** + * 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); - /** - * Take the result value (move it). - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline T&& value() && 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&& take_value() && noexcept(false); + /** + * Take the result value (move it). + * + * @throw simdjson_error if there was an error. + */ + simdjson_inline T&& 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); + /** + * 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; - 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 + /** + * 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; - } // namespace lasx + 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 lasx */ -/* including simdjson/generic/numberparsing.h for lasx: #include "simdjson/generic/numberparsing.h" */ -/* begin file simdjson/generic/numberparsing.h for lasx */ +/* 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 */ @@ -29311,8 +36380,8 @@ namespace simdjson { #include namespace simdjson { - namespace lasx { - namespace numberparsing { +namespace rvv_vls { +namespace numberparsing { #ifdef JSON_TEST_NUMBERS #define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) @@ -29328,540 +36397,547 @@ namespace simdjson { #define BIGINT_NUMBER(SRC) (BIGINT_ERROR) #endif - namespace { +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(negative)) << 63); - std::memcpy(&d, &mantissa, sizeof(d)); - return d; - } +// 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(negative)) << 63); + std::memcpy(&d, &mantissa, sizeof(d)); + return d; +} - // 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 +// 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) + if (0 <= power && power <= 22 && i <= 9007199254740991) #else - if (-22 <= power && power <= 22 && i <= 9007199254740991) + 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 + { + // 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. + // 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; - } + // 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 - // approximatively 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; + // 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 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. - simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]); - // 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. + // 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 - // 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. - simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); - 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); + // 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. - // 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 - } - } + // 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); - mantissa += mantissa & 1; - mantissa >>= 1; + // 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 + } + } - // 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; - } + mantissa += mantissa & 1; + mantissa >>= 1; - // 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(ptr)); - // We do not accept infinite values. + // 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; +} - // 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::max)() || *outDouble < std::numeric_limits::lowest()); - } +// 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(ptr)); + // We do not accept infinite values. - static bool parse_float_fallback(const uint8_t* ptr, const uint8_t* end_ptr, double* outDouble) { - *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(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::max)() || *outDouble < std::numeric_limits::lowest()); +} - // 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::max)() || *outDouble < std::numeric_limits::lowest()); - } +static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) { + *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(end_ptr)); + // We do not accept infinite values. - // 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); - } + // 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::max)() || *outDouble < std::numeric_limits::lowest()); +} - template - 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(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; - } +// 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); +} - simdjson_inline bool is_digit(const uint8_t c) { - return static_cast(c - '0') <= 9; - } +template +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(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 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; +simdjson_inline bool is_digit(const uint8_t c) { + return static_cast(c - '0') <= 9; +} + +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; #ifdef SIMDJSON_SWAR_NUMBER_PARSING #if SIMDJSON_SWAR_NUMBER_PARSING - // this helps if we have lots of decimals! - // this turns out to be frequent enough. - if (is_made_of_eight_digits_fast(p)) { - i = i * 100000000 + parse_eight_digits_unrolled(p); - p += 8; - } + // this helps if we have lots of decimals! + // this turns out to be frequent enough. + if (is_made_of_eight_digits_fast(p)) { + i = i * 100000000 + parse_eight_digits_unrolled(p); + p += 8; + } #endif // SIMDJSON_SWAR_NUMBER_PARSING #endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING - // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) - if (parse_digit(*p, i)) { ++p; } - while (parse_digit(*p, i)) { p++; } - exponent = first_after_period - p; - // Decimal without digits (123.) is illegal - if (exponent == 0) { - return INVALID_NUMBER(src); - } - return SUCCESS; - } + // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) + if (parse_digit(*p, i)) { ++p; } + while (parse_digit(*p, i)) { p++; } + exponent = first_after_period - p; + // Decimal without digits (123.) is illegal + if (exponent == 0) { + return INVALID_NUMBER(src); + } + return SUCCESS; +} - 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 +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. + // 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. + // 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 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; - } + // 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 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); - } +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 +} // unnamed namespace - /** @private */ - static 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 */ +static 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 - 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; - } +/** @private */ +template +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 - simdjson_inline error_code parse_number(const uint8_t* const src, W& writer); +// 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 +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 +// for performance analysis, it is sometimes useful to skip parsing #ifdef SIMDJSON_SKIPNUMBERPARSING - template - simdjson_inline error_code parse_number(const uint8_t* const, W& writer) { - writer.append_s64(0); // always write zero - return SUCCESS; // always succeeds - } +template +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 parse_unsigned(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t* const src) noexcept { return 0; } - simdjson_unused simdjson_inline bool is_negative(const uint8_t* src) noexcept { return false; } - simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t* src) noexcept { return false; } - simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t* src) noexcept { return number_type::signed_integer; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t * const src) noexcept { return 0; } +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { return false; } +simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t * src) noexcept { return number_type::signed_integer; } #else // parse the number at src @@ -29873,751 +36949,760 @@ namespace simdjson { // content and append a space before calling this function. // // Our objective is accurate parsing (ULP of 0) at high speed. - template - 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)) { 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 INVALID_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 { - WRITE_INTEGER(negative ? (~i + 1) : i, src, writer); - } - 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 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; - 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); - // 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 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 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; - 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); - // 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 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; - 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); - // 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 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 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; - while (parse_digit(*src, i)) { src++; } - - // 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 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; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (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 == '.')) { - p++; - const uint8_t* start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (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 = 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; - } - - simdjson_unused simdjson_inline bool is_negative(const uint8_t* src) noexcept { - return (*src == '-'); - } - - simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t* src) noexcept { - bool negative = (*src == '-'); - src += uint8_t(negative); - const uint8_t* p = src; - while (static_cast(*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 get_number_type(const uint8_t* src) noexcept { - bool negative = (*src == '-'); - src += uint8_t(negative); - const uint8_t* p = src; - while (static_cast(*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("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; - } - 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("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 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 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; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (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 == '.')) { - p++; - const uint8_t* start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (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 = 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; - } - - } // unnamed namespace +template +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)) { 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 INVALID_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 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; + 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); + // 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 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 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; + 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); + // 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 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; + 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); + // 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 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 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; + while (parse_digit(*src, i)) { src++; } + + // 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { + return (*src == '-'); +} + +simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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 get_number_type(const uint8_t * src) noexcept { + bool negative = (*src == '-'); + src += uint8_t(negative); + const uint8_t *p = src; + while(static_cast(*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("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("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 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 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; + p += parse_digit(*p, i); + bool leading_zero = (i == 0); + while (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 == '.')) { + p++; + const uint8_t *start_decimal_digits = p; + if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits + p++; + while (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 = 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; +} + +} // unnamed namespace #endif // SIMDJSON_SKIPNUMBERPARSING - } // namespace numberparsing +} // 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; - } +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 rvv_vls } // namespace simdjson #endif // SIMDJSON_GENERIC_NUMBERPARSING_H -/* end file simdjson/generic/numberparsing.h for lasx */ +/* end file simdjson/generic/numberparsing.h for rvv_vls */ -/* 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 */ +/* 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 */ @@ -30627,102 +37712,130 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { +namespace rvv_vls { - // - // internal::implementation_simdjson_result_base inline implementation - // +// +// internal::implementation_simdjson_result_base inline implementation +// - template - simdjson_inline void implementation_simdjson_result_base::tie(T& value, error_code& error) && noexcept { - error = this->second; - if (!error) { - value = std::forward>(*this).first; - } - } +template +simdjson_inline void implementation_simdjson_result_base::tie(T &value, error_code &error) && noexcept { + error = this->second; + if (!error) { + value = std::forward>(*this).first; + } +} - template - simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T& value) && noexcept { - error_code error; - std::forward>(*this).tie(value, error); - return error; - } +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T &value) && noexcept { + error_code error; + std::forward>(*this).tie(value, error); + return error; +} - template - simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { - return this->second; - } +template +simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { + return this->second; +} + + +template +simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base::has_value() const noexcept { + return this->error() == SUCCESS; +} #if SIMDJSON_EXCEPTIONS - template - simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return this->first; - } +template +simdjson_inline T& implementation_simdjson_result_base::operator*() & noexcept(false) { + return this->value(); +} - template - simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { - return std::forward>(*this).take_value(); - } +template +simdjson_inline T&& implementation_simdjson_result_base::operator*() && noexcept(false) { + return std::forward>(*this).value(); +} - template - simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return std::forward(this->first); - } +template +simdjson_inline T* implementation_simdjson_result_base::operator->() noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} - template - simdjson_inline implementation_simdjson_result_base::operator T && () && noexcept(false) { - return std::forward>(*this).take_value(); - } + +template +simdjson_inline const T* implementation_simdjson_result_base::operator->() const noexcept(false) { + if (this->error()) { throw simdjson_error(this->error()); } + return &this->first; +} + +template +simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return this->first; +} + +template +simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { + return std::forward>(*this).take_value(); +} + +template +simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return std::forward(this->first); +} + +template +simdjson_inline implementation_simdjson_result_base::operator T&&() && noexcept(false) { + return std::forward>(*this).take_value(); +} #endif // SIMDJSON_EXCEPTIONS - template - simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { - return this->first; - } +template +simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { + return this->first; +} - template - simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { - return this->first; - } +template +simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { + return this->first; +} - template - simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { - return std::forward(this->first); - } +template +simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { + return std::forward(this->first); +} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T&& value, error_code error) noexcept - : first{ std::forward(value) }, second{ error } {} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept - : implementation_simdjson_result_base(T{}, error) {} - template - simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T&& value) noexcept - : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value, error_code error) noexcept + : first{std::forward(value)}, second{error} {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept + : implementation_simdjson_result_base(T{}, error) {} +template +simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value) noexcept + : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} - } // namespace lasx +} // 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 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 */ +/* 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/lasx/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/base.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ -#undef SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT -/* undefining SIMDJSON_IMPLEMENTATION from "lasx" */ +/* undefining SIMDJSON_IMPLEMENTATION from "rvv_vls" */ #undef SIMDJSON_IMPLEMENTATION -/* end file simdjson/lasx/end.h */ +/* end file simdjson/rvv-vls/end.h */ -#endif // SIMDJSON_LASX_H -/* end file simdjson/lasx.h */ +#endif // SIMDJSON_RVV_VLS_H +/* end file simdjson/rvv-vls.h */ #else #error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION #endif @@ -30734,35 +37847,32 @@ namespace simdjson { /* end file 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 */ +/* 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/ondemand/dependencies.h must be included before defining SIMDJSON_CONDITIONAL_INCLUDE! +#error simdjson/generic/builder/dependencies.h must be included before defining SIMDJSON_CONDITIONAL_INCLUDE! #endif -#ifndef SIMDJSON_GENERIC_ONDEMAND_DEPENDENCIES_H -#define SIMDJSON_GENERIC_ONDEMAND_DEPENDENCIES_H +#ifndef SIMDJSON_GENERIC_BUILDER_DEPENDENCIES_H +#define SIMDJSON_GENERIC_BUILDER_DEPENDENCIES_H -// Internal headers needed for ondemand generics. -// All includes not under simdjson/generic/ondemand must be here! +// Internal headers needed for builder generics. +// All includes not under simdjson/generic/builder 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/padded_string.h" */ -/* skipped duplicate #include "simdjson/padded_string_view.h" */ -/* skipped duplicate #include "simdjson/internal/dom_parser_implementation.h" */ +/* skipped duplicate #include "simdjson/concepts.h" */ +/* skipped duplicate #include "simdjson/dom/fractured_json.h" */ -#endif // SIMDJSON_GENERIC_ONDEMAND_DEPENDENCIES_H -/* end file simdjson/generic/ondemand/dependencies.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/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/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 */ @@ -30778,21 +37888,21 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - /** - * Implementation for NEON (ARMv8). - */ - namespace arm64 { +/** + * Implementation for NEON (ARMv8). + */ +namespace arm64 { - class implementation; +class implementation; - namespace { - namespace simd { - template struct simd8; - template struct simd8x64; - } // namespace simd - } // unnamed namespace +namespace { +namespace simd { +template struct simd8; +template struct simd8x64; +} // namespace simd +} // unnamed namespace - } // namespace arm64 +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_ARM64_BASE_H @@ -30825,100 +37935,106 @@ static_assert(sizeof(uint8x16_t) <= simdjson::SIMDJSON_PADDING, "insufficient pa /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { - namespace { +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) { -#ifdef 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; +// 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); + 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 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) { -#ifdef 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; +// 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); + 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) { - return vaddv_u8(vcnt_u8(vcreate_u8(input_num))); - } +/* result might be undefined when input_num is zero */ +simdjson_inline int count_ones(uint64_t input_num) { + return vaddv_u8(vcnt_u8(vcreate_u8(input_num))); +} #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. - */ +/** + * 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; - } +/* 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); - } +/** + * 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 - simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t* result) { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - * result = value1 + value2; - return *result < value1; +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t *result) { +#if SIMDJSON_REGULAR_VISUAL_STUDIO + *result = value1 + value2; + return *result < value1; #else - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); #endif - } +} - } // unnamed namespace - } // namespace arm64 +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_ARM64_BITMANIPULATION_H @@ -30933,39 +38049,39 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { - namespace { +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; - } +// +// 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 +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif @@ -30989,43 +38105,49 @@ namespace simdjson { #endif // SIMDJSON_REGULAR_VISUAL_STUDIO && SIMDJSON_IS_ARM64 namespace simdjson { - namespace arm64 { - namespace numberparsing { +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); - } +// 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; +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; + // 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 + 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); + __uint128_t r = (static_cast<__uint128_t>(value1)) * value2; + answer.low = uint64_t(r); + answer.high = uint64_t(r >> 64); #endif - return answer; - } + return answer; +} - } // namespace numberparsing - } // namespace arm64 +} // 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 */ @@ -31041,13 +38163,13 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { - namespace { - namespace simd { +namespace arm64 { +namespace { +namespace simd { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - namespace { - // Start of private section with Visual Studio workaround +#if SIMDJSON_REGULAR_VISUAL_STUDIO +namespace { +// Start of private section with Visual Studio workaround #ifndef simdjson_make_uint8x16_t @@ -31099,432 +38221,444 @@ namespace simdjson { #endif // End of private section with Visual Studio workaround - } // namespace +} // namespace #endif // SIMDJSON_REGULAR_VISUAL_STUDIO - template - struct simd8; + template + struct simd8; - // - // Base class of simd8 and simd8, both of which use uint8x16_t internally. - // - template> - struct base_u8 { - uint8x16_t value; - static const int SIZE = sizeof(value); + // + // Base class of simd8 and simd8, both of which use uint8x16_t internally. + // + template> + 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; } + // 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 operator|(const simd8 other) const { return vorrq_u8(*this, other); } - simdjson_inline simd8 operator&(const simd8 other) const { return vandq_u8(*this, other); } - simdjson_inline simd8 operator^(const simd8 other) const { return veorq_u8(*this, other); } - simdjson_inline simd8 bit_andnot(const simd8 other) const { return vbicq_u8(*this, other); } - simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - simdjson_inline simd8& operator|=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast | other; return *this_cast; } - simdjson_inline simd8& operator&=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast & other; return *this_cast; } - simdjson_inline simd8& operator^=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast ^ other; return *this_cast; } + // Bit operations + simdjson_inline simd8 operator|(const simd8 other) const { return vorrq_u8(*this, other); } + simdjson_inline simd8 operator&(const simd8 other) const { return vandq_u8(*this, other); } + simdjson_inline simd8 operator^(const simd8 other) const { return veorq_u8(*this, other); } + simdjson_inline simd8 bit_andnot(const simd8 other) const { return vbicq_u8(*this, other); } + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } + simdjson_inline simd8& operator|=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline simd8& operator&=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline simd8& operator^=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast ^ other; return *this_cast; } - friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return vceqq_u8(lhs, rhs); } + friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return vceqq_u8(lhs, rhs); } - template - simdjson_inline simd8 prev(const simd8 prev_chunk) const { - return vextq_u8(prev_chunk, *this, 16 - N); - } - }; + template + simdjson_inline simd8 prev(const simd8 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 : base_u8 { - typedef uint16_t bitmask_t; - typedef uint32_t bitmask2_t; + // SIMD byte mask type (returned by things like eq and gt) + template<> + struct simd8: base_u8 { + typedef uint16_t bitmask_t; + typedef uint32_t bitmask2_t; - static simdjson_inline simd8 splat(bool _value) { return vmovq_n_u8(uint8_t(-(!!_value))); } + static simdjson_inline simd8 splat(bool _value) { return vmovq_n_u8(uint8_t(-(!!_value))); } - simdjson_inline simd8(const uint8x16_t _value) : base_u8(_value) {} - // False constructor - simdjson_inline simd8() : simd8(vdupq_n_u8(0)) {} - // Splat constructor - simdjson_inline simd8(bool _value) : simd8(splat(_value)) {} + simdjson_inline simd8(const uint8x16_t _value) : base_u8(_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 { -#ifdef 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); + // 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 }; + 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); - } - simdjson_inline bool any() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) != 0; } - }; + 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); + } + simdjson_inline bool any() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) != 0; } + }; - // Unsigned bytes - template<> - struct simd8 : base_u8 { - 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); } + // Unsigned bytes + template<> + struct simd8: base_u8 { + 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(_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 -#ifdef 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 - )) {} + simdjson_inline simd8(const uint8x16_t _value) : base_u8(_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 - }) {} + 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 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( - 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 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( + 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); } + // Store to array + simdjson_inline void store(uint8_t dst[16]) const { return vst1q_u8(dst, *this); } - // Saturated math - simdjson_inline simd8 saturating_add(const simd8 other) const { return vqaddq_u8(*this, other); } - simdjson_inline simd8 saturating_sub(const simd8 other) const { return vqsubq_u8(*this, other); } + // Saturated math + simdjson_inline simd8 saturating_add(const simd8 other) const { return vqaddq_u8(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return vqsubq_u8(*this, other); } - // Addition/subtraction are the same for signed and unsigned - simdjson_inline simd8 operator+(const simd8 other) const { return vaddq_u8(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return vsubq_u8(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *this; } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *this; } + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { return vaddq_u8(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return vsubq_u8(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *this; } + simdjson_inline simd8& operator-=(const simd8 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 max_val(const simd8 other) const { return vmaxq_u8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return vminq_u8(*this, other); } - simdjson_inline simd8 operator<=(const simd8 other) const { return vcleq_u8(*this, other); } - simdjson_inline simd8 operator>=(const simd8 other) const { return vcgeq_u8(*this, other); } - simdjson_inline simd8 operator<(const simd8 other) const { return vcltq_u8(*this, other); } - simdjson_inline simd8 operator>(const simd8 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 gt_bits(const simd8 other) const { return simd8(*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 lt_bits(const simd8 other) const { return simd8(*this < other); } + // 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 max_val(const simd8 other) const { return vmaxq_u8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return vminq_u8(*this, other); } + simdjson_inline simd8 operator<=(const simd8 other) const { return vcleq_u8(*this, other); } + simdjson_inline simd8 operator>=(const simd8 other) const { return vcgeq_u8(*this, other); } + simdjson_inline simd8 operator<(const simd8 other) const { return vcltq_u8(*this, other); } + simdjson_inline simd8 operator>(const simd8 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 gt_bits(const simd8 other) const { return simd8(*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 lt_bits(const simd8 other) const { return simd8(*this < other); } - // Bit-specific operations - simdjson_inline simd8 any_bits_set(simd8 bits) const { return vtstq_u8(*this, bits); } - simdjson_inline bool any_bits_set_anywhere() const { return this->max_val() != 0; } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return (*this & bits).any_bits_set_anywhere(); } - template - simdjson_inline simd8 shr() const { return vshrq_n_u8(*this, N); } - template - simdjson_inline simd8 shl() const { return vshlq_n_u8(*this, N); } + // Bit-specific operations + simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return (*this & bits).any_bits_set_anywhere(); } + template + simdjson_inline simd8 shr() const { return vshrq_n_u8(*this, N); } + template + simdjson_inline simd8 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 - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return lookup_table.apply_lookup_16_to(*this); - } + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 lookup_table) const { + return lookup_table.apply_lookup_16_to(*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 compress(uint16_t mask) would be - // sensible, but the AVX ISA makes this kind of approach difficult. - template - 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 -#ifdef 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); + // 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 compress(uint16_t mask) would be + // sensible, but the AVX ISA makes this kind of approach difficult. + template + 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 }; + 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(pshufb_combine_table + pop1 * 8)); - uint8x16_t answer = vqtbl1q_u8(pruned, compactmask); - vst1q_u8(reinterpret_cast(output), answer); - } + 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(pshufb_combine_table + pop1 * 8)); + uint8x16_t answer = vqtbl1q_u8(pruned, compactmask); + vst1q_u8(reinterpret_cast(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 - 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 -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - uint8x8_t inc = simdjson_make_uint8x8_t(0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08); + // 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 + 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 }; + 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)); - } + 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 - simdjson_inline simd8 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::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } - template - simdjson_inline simd8 apply_lookup_16_to(const simd8 original) { - return vqtbl1q_u8(*this, simd8(original)); - } - }; + template + simdjson_inline simd8 apply_lookup_16_to(const simd8 original) { + return vqtbl1q_u8(*this, simd8(original)); + } + }; - // Signed bytes - template<> - struct simd8 { - int8x16_t value; + // Signed bytes + template<> + struct simd8 { + int8x16_t value; - static simdjson_inline simd8 splat(int8_t _value) { return vmovq_n_s8(_value); } - static simdjson_inline simd8 zero() { return vdupq_n_s8(0); } - static simdjson_inline simd8 load(const int8_t values[16]) { return vld1q_s8(values); } + static simdjson_inline simd8 splat(int8_t _value) { return vmovq_n_s8(_value); } + static simdjson_inline simd8 zero() { return vdupq_n_s8(0); } + static simdjson_inline simd8 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; } + // 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 -#ifdef 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 - )) {} + // 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 - }) {} + 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 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( - 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 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( + 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); } + // 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. + // 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)) {} + simdjson_inline explicit simd8(const uint8x16_t other): simd8(vreinterpretq_s8_u8(other)) {} #endif - simdjson_inline explicit operator simd8() const { return vreinterpretq_u8_s8(this->value); } + simdjson_inline explicit operator simd8() const { return vreinterpretq_u8_s8(this->value); } - // Math - simdjson_inline simd8 operator+(const simd8 other) const { return vaddq_s8(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return vsubq_s8(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *this; } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *this; } + // Math + simdjson_inline simd8 operator+(const simd8 other) const { return vaddq_s8(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return vsubq_s8(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *this; } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *this; } - // Order-sensitive comparisons - simdjson_inline simd8 max_val(const simd8 other) const { return vmaxq_s8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return vminq_s8(*this, other); } - simdjson_inline simd8 operator>(const simd8 other) const { return vcgtq_s8(*this, other); } - simdjson_inline simd8 operator<(const simd8 other) const { return vcltq_s8(*this, other); } - simdjson_inline simd8 operator==(const simd8 other) const { return vceqq_s8(*this, other); } + // Order-sensitive comparisons + simdjson_inline simd8 max_val(const simd8 other) const { return vmaxq_s8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return vminq_s8(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return vcgtq_s8(*this, other); } + simdjson_inline simd8 operator<(const simd8 other) const { return vcltq_s8(*this, other); } + simdjson_inline simd8 operator==(const simd8 other) const { return vceqq_s8(*this, other); } - template - simdjson_inline simd8 prev(const simd8 prev_chunk) const { - return vextq_s8(prev_chunk, *this, 16 - N); - } + template + simdjson_inline simd8 prev(const simd8 prev_chunk) const { + return vextq_s8(prev_chunk, *this, 16 - N); + } - // Perform a lookup assuming no value is larger than 16 - template - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return lookup_table.apply_lookup_16_to(*this); - } - template - simdjson_inline simd8 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::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } + // Perform a lookup assuming no value is larger than 16 + template + simdjson_inline simd8 lookup_16(simd8 lookup_table) const { + return lookup_table.apply_lookup_16_to(*this); + } + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } - template - simdjson_inline simd8 apply_lookup_16_to(const simd8 original) { - return vqtbl1q_s8(*this, simd8(original)); - } - }; + template + simdjson_inline simd8 apply_lookup_16_to(const simd8 original) { + return vqtbl1q_s8(*this, simd8(original)); + } + }; - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 4, "ARM kernel should use four registers per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 4, "ARM kernel should use four registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{ chunk0, chunk1, chunk2, chunk3 } {} - simdjson_inline simd8x64(const T ptr[64]) : chunks{ simd8::load(ptr), simd8::load(ptr + 16), simd8::load(ptr + 32), simd8::load(ptr + 48) } {} + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::load(ptr+16), simd8::load(ptr+32), simd8::load(ptr+48)} {} - simdjson_inline void store(T ptr[64]) const { - this->chunks[0].store(ptr + sizeof(simd8) * 0); - this->chunks[1].store(ptr + sizeof(simd8) * 1); - this->chunks[2].store(ptr + sizeof(simd8) * 2); - this->chunks[3].store(ptr + sizeof(simd8) * 3); - } + simdjson_inline void store(T ptr[64]) const { + this->chunks[0].store(ptr+sizeof(simd8)*0); + this->chunks[1].store(ptr+sizeof(simd8)*1); + this->chunks[2].store(ptr+sizeof(simd8)*2); + this->chunks[3].store(ptr+sizeof(simd8)*3); + } - simdjson_inline simd8 reduce_or() const { - return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]); - } + simdjson_inline simd8 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 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 { -#ifdef 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 - ); + 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 - }; + 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); - } + // 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] == mask, - this->chunks[1] == mask, - this->chunks[2] == mask, - this->chunks[3] == mask - ).to_bitmask(); - } + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] <= mask, - this->chunks[1] <= mask, - this->chunks[2] <= mask, - this->chunks[3] <= mask - ).to_bitmask(); - } - }; // struct simd8x64 + simdjson_inline uint64_t lteq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] <= mask, + this->chunks[1] <= mask, + this->chunks[2] <= mask, + this->chunks[3] <= mask + ).to_bitmask(); + } + }; // struct simd8x64 - } // namespace simd - } // unnamed namespace - } // namespace arm64 +} // namespace simd +} // unnamed namespace +} // namespace arm64 } // namespace simdjson #endif // SIMDJSON_ARM64_SIMD_H @@ -31541,46 +38675,72 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace arm64 { - namespace { +namespace arm64 { +namespace { - using namespace simd; +using namespace simd; - // Holds backslashes and quotes locations. - struct backslash_and_quote { - public: - static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t* src, uint8_t* dst); +// 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); } + 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 + 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 v0(src); - simd8 v1(src + sizeof(v0)); - v0.store(dst); - v1.store(dst + sizeof(v0)); +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 v0(src); + simd8 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(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); - return { - uint32_t(bs_and_quote), // bs_bits - uint32_t(bs_and_quote >> 32) // quote_bits - }; - } + // 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(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); + return { + uint32_t(bs_and_quote), // bs_bits + uint32_t(bs_and_quote >> 32) // quote_bits + }; +} - } // unnamed namespace - } // namespace arm64 +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 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 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 @@ -31588,10301 +38748,1674 @@ namespace simdjson { #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! +/* 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 -// 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 +/* 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_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; - - } // 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/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): #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 { - 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; +#if SIMDJSON_SUPPORTS_CONCEPTS - /** - * Denote that we're starting a document. - */ - simdjson_inline void start_document() noexcept; +namespace arm64 { +namespace builder { + class string_builder; +}} - /** - * 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; +template +struct has_custom_serialization : std::false_type {}; - /** - * Tell whether the iterator is at the EOF mark - */ - simdjson_inline bool at_end() const noexcept; +inline constexpr struct serialize_tag { + template + constexpr void operator()(arm64::builder::string_builder& b, T&& obj) const{ + return tag_invoke(*this, b, std::forward(obj)); + } - /** - * 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; +} serialize{}; +template +struct has_custom_serialization(), std::declval())) +>> : std::true_type {}; - /** - * 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 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 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 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). - */ - simdjson_warn_unused simdjson_inline simdjson_result 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). - */ - simdjson_warn_unused simdjson_inline simdjson_result 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 has_next_field() noexcept; - - /** - * Get the current field's key. - */ - simdjson_warn_unused simdjson_inline simdjson_result field_key() 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 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 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 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 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). - */ - simdjson_warn_unused simdjson_inline simdjson_result 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). - */ - simdjson_warn_unused simdjson_inline simdjson_result 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 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 get_string(bool allow_replacement) noexcept; - template - simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_int64() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_double() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_bool() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_null() noexcept; - simdjson_warn_unused simdjson_inline bool is_negative() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_integer() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_number() noexcept; - - simdjson_warn_unused simdjson_inline simdjson_result get_root_string(bool check_trailing, bool allow_replacement) noexcept; - template - 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 get_root_wobbly_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_raw_json_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64_in_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_int64(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_int64_in_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_double(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_double_in_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_bool(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline bool is_root_negative() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_root_integer(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_number_type(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_number(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_root_null(bool check_trailing) noexcept; - - 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 reset_array() noexcept; - /** - * Restarts an object iteration. - * @returns Whether the object has any fields (returns false for empty). - */ - simdjson_inline simdjson_result 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 parse_null(const uint8_t* json) const noexcept; - simdjson_inline simdjson_result 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_inline error_code start_container(uint8_t start_char, const char* incorrect_type_message, const char* type) noexcept; - 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 advance_to_value() noexcept; - - simdjson_inline error_code incorrect_type_error(const char* message) const noexcept; - 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; - /** @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_inline error_code report_error(error_code error, const char* message) noexcept; - - friend class document; - friend class object; - friend class array; - friend class value; - friend class field; - }; // value_iterator - - } // namespace ondemand - } // namespace arm64 -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public arm64::implementation_simdjson_result_base { - 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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -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, uint64_t, int64_t, double, bool - * - * You may use get_double(), get_bool(), get_uint64(), get_int64(), - * 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 simdjson_inline simdjson_result get() noexcept { - // 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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " 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."); - } - - /** - * Get this value as the given type. - * - * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool - * - * @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 simdjson_inline error_code get(T& out) noexcept; - - /** - * 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 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 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 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 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 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 get_int64_in_string() 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 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 get_double_in_string() noexcept; - - /** - * Cast this JSON value to a string. - * - * The string is guaranteed to be valid UTF-8. - * - * Equivalent to get(). - * - * Important: a value should be consumed once. Calling get_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 get_string(bool allow_replacement = false) noexcept; - - /** - * 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 - 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 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 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 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 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 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 - 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 signed 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(). - * - * @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 begin() & noexcept; - /** - * Sentinel representing the end of the array. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result 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 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 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 at(size_t index) noexcept; - /** - * Look up a field by name on an object (order-sensitive). - * - * 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 }`: - * - * ```c++ - * 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 find_field(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ - simdjson_inline simdjson_result 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. - * - * It is the default, however, 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 find_field_unordered(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result find_field_unordered(const char* key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](const char* key) noexcept; - - /** - * 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 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 is_scalar() noexcept; - /** - * Checks whether the value is a string. - * - * @returns true if the type is string - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". - */ - simdjson_inline simdjson_result is_string() noexcept; - - /** - * Checks whether the value is a negative number. - * - * @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 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 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 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 raw_json() noexcept; - - /** - * Returns the current location in the document if in bounds. - */ - simdjson_inline simdjson_result 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 at_pointer(std::string_view json_pointer) noexcept; - - /** - * Get the value associated with the given JSONPath expression. We only support - * JSONPath queries that trivially convertible to JSON Pointer queries: key - * names and array indices. - * - * @return The value associated with the given JSONPath expression, or: - * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - */ - simdjson_inline simdjson_result at_path(std::string_view at_path) noexcept; - - - protected: - /** - * Create a value. - */ - 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 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; - friend struct simdjson_result; - friend class field; - }; - - } // namespace ondemand - } // namespace arm64 -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public arm64::implementation_simdjson_result_base { - 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 get_array() noexcept; - simdjson_inline simdjson_result get_object() noexcept; - - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result is_null() noexcept; - - template simdjson_inline simdjson_result get() noexcept; - - template simdjson_inline error_code get(T& out) noexcept; - -#if SIMDJSON_EXCEPTIONS - template - 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 count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - - /** - * Look up a field by name on an object (order-sensitive). - * - * 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 }`: - * - * ```c++ - * 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 find_field(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ - simdjson_inline simdjson_result 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. - * - * It is the default, however, 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 find_field_unordered(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result find_field_unordered(const char* key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](const char* key) noexcept; - - /** - * Get the type of this JSON value. - * - * 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 type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_string() noexcept; - simdjson_inline simdjson_result is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - - /** @copydoc simdjson_inline std::string_view value::raw_json_token() const noexcept */ - simdjson_inline simdjson_result raw_json_token() noexcept; - simdjson_inline simdjson_result raw_json() noexcept; - - /** @copydoc simdjson_inline simdjson_result current_location() noexcept */ - simdjson_inline simdjson_result current_location() noexcept; - /** @copydoc simdjson_inline int32_t current_depth() const noexcept */ - simdjson_inline simdjson_result current_depth() const noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - }; - -} // namespace simdjson - -#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; +template +constexpr bool require_custom_serialization = has_custom_serialization::value; #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 - 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 - 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 (`{` `}` `[` `]` `,` `:` `""` `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 value_iterator; - friend class object; - template - friend simdjson_inline void logger::log_line(const json_iterator& iter, const char* title_prefix, const char* title, std::string_view detail, int delta, int depth_delta, logger::log_level level, Args&&... args) noexcept; - template - 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 : public arm64::implementation_simdjson_result_base { - 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 }; - - 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; - - /** - * 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_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_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 unescape(raw_json_string in, bool allow_replacement) noexcept; - simdjson_inline simdjson_result unescape_wobbly(raw_json_string in) noexcept; - - simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept; - - 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 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 - 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 - 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 : public arm64::implementation_simdjson_result_base { - 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 { - // 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 - 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 - 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& type) noexcept(false); -#endif - - } // namespace ondemand - } // namespace arm64 -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public arm64::implementation_simdjson_result_base { - 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; - - /** - * 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 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 unescape_wobbly(json_iterator& iter) const noexcept; - const uint8_t* buf{}; - friend class object; - friend class field; - friend class parser; - friend struct simdjson_result; - }; - - 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 : public arm64::implementation_simdjson_result_base { - 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 raw() const noexcept; - simdjson_inline simdjson_warn_unused simdjson_result unescape(arm64::ondemand::json_iterator& iter, bool allow_replacement) const noexcept; - simdjson_inline simdjson_warn_unused simdjson_result 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 - -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. - * - * @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 iterate(padded_string_view json) & noexcept; -#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON - simdjson_warn_unused simdjson_result iterate_allow_incomplete_json(padded_string_view json) & noexcept; -#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const char* json, size_t len, size_t capacity) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const uint8_t* json, size_t len, size_t capacity) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(std::string_view json, size_t capacity) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const std::string& json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(std::string& json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const simdjson_result& json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const simdjson_result& json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result 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 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. - * - * ### 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. - * @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 10MB, which has been a reasonable sweet spot in our tests. - * @param allow_comma_separated (defaults on false) This allows a mode where the documents are - * separated by commas instead of whitespace. It comes with a performance - * penalty because the entire document is indexed at once (and the document must be - * less than 4 GB), and there is no multithreading. In this mode, the batch_size parameter - * is effectively ignored, as it is set to at least the document size. - * @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 iterate_many(const uint8_t* buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ - inline simdjson_result iterate_many(const char* buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ - inline simdjson_result iterate_many(const std::string& s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - inline simdjson_result iterate_many(const std::string&& s, size_t batch_size, bool allow_comma_separated = false) = delete;// unsafe - /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ - inline simdjson_result iterate_many(const padded_string& s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - inline simdjson_result iterate_many(const padded_string&& s, size_t batch_size, bool allow_comma_separated = false) = delete;// unsafe - - /** @private We do not want to allow implicit conversion from C string to std::string. */ - simdjson_result iterate_many(const char* buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete; - - /** 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 }; -#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 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 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 - - private: - /** @private [for benchmarking access] The implementation to use */ - std::unique_ptr implementation{}; - size_t _capacity{ 0 }; - size_t _max_capacity; - size_t _max_depth{ DEFAULT_MAX_DEPTH }; - std::unique_ptr string_buf{}; -#if SIMDJSON_DEVELOPMENT_CHECKS - std::unique_ptr start_positions{}; -#endif - - friend class json_iterator; - friend class document_stream; - }; - - } // namespace ondemand - } // namespace arm64 -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public arm64::implementation_simdjson_result_base { - 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): #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. - */ - simdjson_inline simdjson_result begin() noexcept; - /** - * Sentinel representing the end of the array. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result 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. - * - * To check that an array is empty, it is more performant to use - * the is_empty() method. - */ - simdjson_inline simdjson_result 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 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 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 at_pointer(std::string_view json_pointer) noexcept; - - /** - * Get the value associated with the given JSONPath expression. We only support - * JSONPath queries that trivially convertible to JSON Pointer queries: key - * names and array indices. - * - * https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00 - * - * @return The value associated with the given JSONPath expression, or: - * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - */ - inline simdjson_result at_path(std::string_view json_path) noexcept; - - /** - * Consumes the array and returns a string_view instance corresponding to the - * array as represented in JSON. It points inside the original document. - */ - simdjson_inline simdjson_result 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 at(size_t index) noexcept; - protected: - /** - * Go to the end of the array, no matter where you are right now. - */ - 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 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 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 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{}; - - friend class value; - friend class document; - friend struct simdjson_result; - friend struct simdjson_result; - friend class array_iterator; - }; - - } // namespace ondemand - } // namespace arm64 -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public arm64::implementation_simdjson_result_base { - 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 begin() noexcept; - simdjson_inline simdjson_result end() noexcept; - inline simdjson_result count_elements() & noexcept; - inline simdjson_result is_empty() & noexcept; - inline simdjson_result reset() & noexcept; - simdjson_inline simdjson_result at(size_t index) noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - simdjson_inline simdjson_result raw_json() noexcept; - - }; - -} // 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 "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 exactly once per element. - * - ++ must be called exactly once in between each * (*, ++, *, ++, * ...) - */ - class array_iterator { - public: - /** Create a new, invalid array iterator. */ - simdjson_inline array_iterator() noexcept = default; - - // - // Iterator interface - // - - /** - * Get the current element. - * - * Part of the std::iterator interface. - */ - simdjson_inline simdjson_result 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; - - private: - value_iterator iter{}; - - simdjson_inline array_iterator(const value_iterator& iter) noexcept; - - friend class array; - friend class value; - friend struct simdjson_result; - }; - - } // namespace ondemand - } // namespace arm64 -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public arm64::implementation_simdjson_result_base { - public: - 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 operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. - simdjson_inline bool operator==(const simdjson_result&) const noexcept; - simdjson_inline bool operator!=(const simdjson_result&) const noexcept; - simdjson_inline simdjson_result& operator++() 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): #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 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 get_object() & noexcept; - /** - * Cast this JSON value to an unsigned integer. - * - * @returns A signed 64-bit integer. - * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. - */ - simdjson_inline simdjson_result get_uint64() noexcept; - /** - * Cast this JSON value (inside string) to an unsigned integer. - * - * @returns A signed 64-bit integer. - * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. - */ - simdjson_inline simdjson_result 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 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 get_int64_in_string() 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 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 get_double_in_string() noexcept; - /** - * 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 get_string(bool allow_replacement = false) noexcept; - /** - * 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 - 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 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 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 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 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 is_null() noexcept; - - /** - * Get this value as the given type. - * - * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool - * - * You may use get_double(), get_bool(), get_uint64(), get_int64(), - * 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 simdjson_inline simdjson_result get() & noexcept { - // 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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " 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."); - } - /** @overload template simdjson_result get() & noexcept */ - template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept { - // 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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " 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."); - } - - /** - * Get this value as the given type. - * - * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 simdjson_inline error_code get(T& out) & noexcept; - /** @overload template error_code get(T &out) & noexcept */ - template simdjson_deprecated simdjson_inline error_code get(T& out) && noexcept; - -#if SIMDJSON_EXCEPTIONS - /** - * Cast this JSON value to an instance of type T. The programmer is responsible for - * providing an implementation of get 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 - explicit simdjson_inline operator T() & noexcept(false); - template - 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 signed 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. - * - * @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); - /** - * 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. - */ - simdjson_inline simdjson_result 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 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 at(size_t index) & noexcept; - /** - * Begin array iteration. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result begin() & noexcept; - /** - * Sentinel representing the end of the array. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result end() & noexcept; - - /** - * Look up a field by name on an object (order-sensitive). - * - * 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 }`: - * - * ```c++ - * 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 find_field(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result 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. - * - * It is the default, however, 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 find_field_unordered(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result find_field_unordered(const char* key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](const char* key) & noexcept; - - /** - * 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). - * - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". - */ - simdjson_inline simdjson_result 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 is_scalar() noexcept; - - /** - * Checks whether the document is a string. - * - * @returns true if the type is string - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". - */ - simdjson_inline simdjson_result is_string() noexcept; - - /** - * Checks whether the document is a negative number. - * - * @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 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 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 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 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 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 at_pointer(std::string_view json_pointer) noexcept; - - /** - * Get the value associated with the given JSONPath expression. We only support - * JSONPath queries that trivially convertible to JSON Pointer queries: key - * names and array indices. - * - * https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00 - * - * Key values are matched exactly, without unescaping or Unicode normalization. - * We do a byte-by-byte comparison. E.g. - * - * const padded_string json = "{\"\\u00E9\":123}"_padded; - * auto doc = parser.iterate(json); - * doc.at_path(".\\u00E9") == 123 - * doc.at_path((const char*)u8".\u00E9") returns an error (NO_SUCH_FIELD) - * - * @return The value associated with the given JSONPath expression, or: - * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - */ - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - - /** - * Consumes the document and returns a string_view instance corresponding to the - * document as represented in JSON. It points inside the original byte array containing - * the JSON document. - */ - simdjson_inline simdjson_result raw_json() noexcept; - protected: - /** - * Consumes the document. - */ - 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 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. - */ - 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 get_array() & noexcept; - simdjson_inline simdjson_result get_object() & noexcept; - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result get_value() noexcept; - - simdjson_inline simdjson_result is_null() noexcept; - template simdjson_inline simdjson_result get() & noexcept; - simdjson_inline simdjson_result raw_json() noexcept; - simdjson_inline operator document& () const noexcept; -#if SIMDJSON_EXCEPTIONS - template - explicit simdjson_inline operator T() noexcept(false); - simdjson_inline operator array() & noexcept(false); - simdjson_inline operator 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 raw_json_string() noexcept(false); - simdjson_inline operator bool() noexcept(false); - simdjson_inline operator value() noexcept(false); -#endif - simdjson_inline simdjson_result count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) & noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(const char* key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](const char* key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(const char* key) & noexcept; - - simdjson_inline simdjson_result type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_string() noexcept; - - simdjson_inline simdjson_result current_location() noexcept; - simdjson_inline int32_t current_depth() const noexcept; - simdjson_inline bool is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - simdjson_inline simdjson_result raw_json_token() noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - - private: - document* doc{ nullptr }; - }; - } // namespace ondemand - } // namespace arm64 -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public arm64::implementation_simdjson_result_base { - 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 get_array() & noexcept; - simdjson_inline simdjson_result get_object() & noexcept; - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result get_value() noexcept; - simdjson_inline simdjson_result is_null() noexcept; - - template simdjson_inline simdjson_result get() & noexcept; - template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept; - - template simdjson_inline error_code get(T& out) & noexcept; - template simdjson_inline error_code get(T& out) && noexcept; -#if SIMDJSON_EXCEPTIONS - template ::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); - 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); - simdjson_inline operator arm64::ondemand::value() noexcept(false); -#endif - simdjson_inline simdjson_result count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) & noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(const char* key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](const char* key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(const char* key) & noexcept; - simdjson_inline simdjson_result type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_string() noexcept; - simdjson_inline simdjson_result current_location() noexcept; - simdjson_inline int32_t current_depth() const noexcept; - simdjson_inline bool at_end() const noexcept; - simdjson_inline bool is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - /** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */ - simdjson_inline simdjson_result raw_json_token() noexcept; - - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - }; - - -} // namespace simdjson - - - -namespace simdjson { - - template<> - struct simdjson_result : public arm64::implementation_simdjson_result_base { - 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 get_array() & noexcept; - simdjson_inline simdjson_result get_object() & noexcept; - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result get_value() noexcept; - simdjson_inline simdjson_result is_null() noexcept; -#if SIMDJSON_EXCEPTIONS - template ::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); - 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); - simdjson_inline operator arm64::ondemand::value() noexcept(false); -#endif - simdjson_inline simdjson_result count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) & noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(const char* key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](const char* key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(const char* key) & noexcept; - simdjson_inline simdjson_result type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_string() noexcept; - simdjson_inline simdjson_result current_location() noexcept; - simdjson_inline simdjson_result current_depth() const noexcept; - simdjson_inline simdjson_result is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - /** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */ - simdjson_inline simdjson_result raw_json_token() noexcept; - - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - }; - - -} // 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 -#include -#include -#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. - * - * ```c++ - * 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(); - * - */ - inline size_t truncated_bytes() const noexcept; - - class iterator { - public: - using value_type = simdjson_result; - using reference = simdjson_result; - using pointer = void; - 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 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. - * - * 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; - - 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; - }; - - /** - * 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) - */ - simdjson_inline document_stream( - ondemand::parser& parser, - const uint8_t* buf, - size_t len, - size_t batch_size, - bool allow_comma_separated - ) 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. */ - inline void next_document() 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; - /** - * 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 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. - */ - 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; - friend struct simdjson::internal::simdjson_result_base; - }; // document_stream - - } // namespace ondemand - } // namespace arm64 -} // namespace simdjson - -namespace simdjson { - template<> - struct simdjson_result : public arm64::implementation_simdjson_result_base { - 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 so you can use C++ algorithms that rely on pairs. - */ - class field : public std::pair { - 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 unescaped_key(bool allow_replacement = false) 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. 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 - 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; - /** - * 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 start(value_iterator& parent_iter) noexcept; - static simdjson_inline simdjson_result start(const value_iterator& parent_iter, raw_json_string key) noexcept; - friend struct simdjson_result; - friend class object_iterator; - }; - - } // namespace ondemand - } // namespace arm64 -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public arm64::implementation_simdjson_result_base { - 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 unescaped_key(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code unescaped_key(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result key() noexcept; - simdjson_inline simdjson_result key_raw_json_token() noexcept; - simdjson_inline simdjson_result escaped_key() noexcept; - simdjson_inline simdjson_result value() noexcept; - }; - -} // namespace simdjson - -#endif // SIMDJSON_GENERIC_ONDEMAND_FIELD_H -/* end file simdjson/generic/ondemand/field.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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -namespace simdjson { - namespace arm64 { - namespace ondemand { - - /** - * 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; - - simdjson_inline simdjson_result begin() noexcept; - simdjson_inline simdjson_result end() noexcept; - /** - * Look up a field by name on an object (order-sensitive). - * - * 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 }`: - * - * ```c++ - * 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 }`. - * - * 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 find_field(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result 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. - * - * It is the default, however, 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 find_field_unordered(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) && 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("/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 at_pointer(std::string_view json_pointer) noexcept; - - /** - * Get the value associated with the given JSONPath expression. We only support - * JSONPath queries that trivially convertible to JSON Pointer queries: key - * names and array indices. - * - * @return The value associated with the given JSONPath expression, or: - * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - */ - inline simdjson_result at_path(std::string_view json_path) noexcept; - - /** - * Reset the iterator so that we are pointing back at the - * beginning of the object. You should still consume values only once even if you - * 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 reset() & 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 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 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 raw_json() noexcept; - - protected: - /** - * Go to the end of the object, no matter where you are right now. - */ - simdjson_inline error_code consume() noexcept; - static simdjson_inline simdjson_result start(value_iterator& iter) noexcept; - static simdjson_inline simdjson_result start_root(value_iterator& iter) noexcept; - static simdjson_inline simdjson_result 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{}; - - friend class value; - friend class document; - friend struct simdjson_result; - }; - - } // namespace ondemand - } // namespace arm64 -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public arm64::implementation_simdjson_result_base { - 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 begin() noexcept; - simdjson_inline simdjson_result end() noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) && noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - - inline simdjson_result reset() noexcept; - inline simdjson_result is_empty() noexcept; - inline simdjson_result count_fields() & noexcept; - inline simdjson_result raw_json() noexcept; - - }; - -} // 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; - - // - // Iterator interface - // - - // Reads key and value, yielding them to the user. - // MUST ONLY BE CALLED ONCE PER ITERATION. - simdjson_inline simdjson_result 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 ',' - simdjson_inline object_iterator& operator++() noexcept; - - private: - /** - * 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; - friend class object; - }; - - } // namespace ondemand - } // namespace arm64 -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public arm64::implementation_simdjson_result_base { - 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 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&) const noexcept; - // Assumes it's being compared with the end. true if depth >= iter->depth. - simdjson_inline bool operator!=(const simdjson_result&) const noexcept; - // Checks for ']' and ',' - simdjson_inline simdjson_result& 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/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 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 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 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 to_json_string(arm64::ondemand::array& x) noexcept; - inline simdjson_result to_json_string(simdjson_result x); - inline simdjson_result to_json_string(simdjson_result x); - inline simdjson_result to_json_string(simdjson_result x); - inline simdjson_result to_json_string(simdjson_result x); -} // namespace simdjson +struct has_custom_serialization : std::false_type {}; +#endif // SIMDJSON_SUPPORTS_CONCEPTS +namespace arm64 { +namespace builder { /** - * 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 + * 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. */ -namespace simdjson { - namespace arm64 { - namespace ondemand { +class string_builder { +public: + simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY); - /** - * 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); + 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::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 + 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 + simdjson_inline void append_key_value(key_type key, value_type value) noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + template + simdjson_inline void append_key_value(value_type value) noexcept; + + // Support for optional types (std::optional, etc.) + template + requires(!require_custom_serialization) + simdjson_inline void append(const T &opt); + + template + requires(require_custom_serialization) + simdjson_inline void append(T &&val); + + // Support for string-like types + template + requires(std::is_convertible::value || + std::is_same::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 +requires (!std::is_convertible::value && !require_custom_serialization) + 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; #if SIMDJSON_EXCEPTIONS - inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result 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 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&& 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&& 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 x); -#endif - } - } -} // namespace simdjson::arm64::ondemand - -#endif // SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H -/* end file simdjson/generic/ondemand/serialization.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/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::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::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::started(value_iterator& iter) noexcept { - bool has_value; - SIMDJSON_TRY(iter.started_array().get(has_value)); - return array(iter); - } - - simdjson_inline simdjson_result array::begin() noexcept { -#if SIMDJSON_DEVELOPMENT_CHECKS - if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } -#endif - return array_iterator(iter); - } - simdjson_inline simdjson_result array::end() noexcept { - return array_iterator(iter); - } - 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 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(starting_point), size_t(final_point - starting_point)); - } - - SIMDJSON_PUSH_DISABLE_WARNINGS - SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING - simdjson_inline simdjson_result array::count_elements() & 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 array after counting the number of elements. - iter.reset_array(); - return count; - } - SIMDJSON_POP_DISABLE_WARNINGS - - simdjson_inline simdjson_result array::is_empty() & noexcept { - bool is_not_empty; - auto error = iter.reset_array().get(is_not_empty); - if (error) { return error; } - return !is_not_empty; - } - - inline simdjson_result array::reset() & noexcept { - return iter.reset_array(); - } - - inline simdjson_result array::at_pointer(std::string_view json_pointer) noexcept { - if (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; } - - // Read the array index - size_t array_index = 0; - size_t i; - for (i = 0; i < json_pointer.length() && json_pointer[i] != '/'; i++) { - uint8_t digit = uint8_t(json_pointer[i] - '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; } - array_index = array_index * 10 + digit; - } - - // 0 followed by other digits is invalid - if (i > 1 && json_pointer[0] == '0') { return INVALID_JSON_POINTER; } // "JSON pointer array index has other characters after 0" - - // Empty string is invalid; so is a "/" with no digits before it - if (i == 0) { return INVALID_JSON_POINTER; } // "Empty string in JSON pointer array index" - // 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 std::string json_path_to_pointer_conversion(std::string_view json_path) { - if (json_path.empty() || (json_path.front() != '.' && - json_path.front() != '[')) { - return "-1"; // This is just a sentinel value, the caller should check for this and return an error. - } - - std::string result; - // Reserve space to reduce allocations, adjusting for potential increases due - // to escaping. - result.reserve(json_path.size() * 2); - - size_t i = 0; - - while (i < json_path.length()) { - if (json_path[i] == '.') { - result += '/'; - } - else if (json_path[i] == '[') { - result += '/'; - ++i; // Move past the '[' - while (i < json_path.length() && json_path[i] != ']') { - if (json_path[i] == '~') { - result += "~0"; - } - else if (json_path[i] == '/') { - result += "~1"; - } - else { - result += json_path[i]; - } - ++i; - } - if (i == json_path.length() || json_path[i] != ']') { - return "-1"; // Using sentinel value that will be handled as an error by the caller. - } - } - else { - if (json_path[i] == '~') { - result += "~0"; - } - else if (json_path[i] == '/') { - result += "~1"; - } - else { - result += json_path[i]; - } - } - ++i; - } - - return result; - } - - inline simdjson_result array::at_path(std::string_view json_path) noexcept { - auto json_pointer = json_path_to_pointer_conversion(json_path); - if (json_pointer == "-1") { return INVALID_JSON_POINTER; } - return at_pointer(json_pointer); - } - - simdjson_inline simdjson_result array::at(size_t index) noexcept { - size_t i = 0; - for (auto value : *this) { - if (i == index) { return value; } - i++; - } - return INDEX_OUT_OF_BOUNDS; - } - - } // namespace ondemand - } // namespace arm64 -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result( - arm64::ondemand::array&& value - ) noexcept - : implementation_simdjson_result_base( - std::forward(value) - ) - { - } - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept - : implementation_simdjson_result_base(error) - { - } - - simdjson_inline simdjson_result simdjson_result::begin() noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() noexcept { - if (error()) { return error(); } - return first.end(); - } - simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); - } - simdjson_inline simdjson_result simdjson_result::is_empty() & noexcept { - if (error()) { return error(); } - return first.is_empty(); - } - simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } - simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { return error(); } - return first.at_path(json_path); - } - simdjson_inline simdjson_result simdjson_result::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 } - {} - - simdjson_inline simdjson_result array_iterator::operator*() noexcept { - 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 { - 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; - } - - } // namespace ondemand - } // namespace arm64 -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result( - arm64::ondemand::array_iterator&& value - ) noexcept - : arm64::implementation_simdjson_result_base(std::forward(value)) - { - first.iter.assert_is_valid(); - } - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : arm64::implementation_simdjson_result_base({}, error) - { - } - - simdjson_inline simdjson_result simdjson_result::operator*() noexcept { - if (error()) { return error(); } - return *first; - } - simdjson_inline bool simdjson_result::operator==(const simdjson_result& other) const noexcept { - if (!first.iter.is_valid()) { return !error(); } - return first == other.first; - } - simdjson_inline bool simdjson_result::operator!=(const simdjson_result& other) const noexcept { - if (!first.iter.is_valid()) { return error(); } - return first != other.first; - } - simdjson_inline simdjson_result& simdjson_result::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_ARRAY_ITERATOR_INL_H -/* end file simdjson/generic/ondemand/array_iterator-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/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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -namespace simdjson { - namespace arm64 { - namespace ondemand { - - simdjson_inline document::document(ondemand::json_iterator&& _iter) noexcept - : iter{ std::forward(_iter) } - { - logger::log_start_value(iter, "document"); - } - - simdjson_inline document document::start(json_iterator&& iter) noexcept { - return document(std::forward(iter)); - } - - inline void document::rewind() noexcept { - iter.rewind(); - } - - inline std::string document::to_debug_string() noexcept { - return iter.to_string(); - } - - inline simdjson_result 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 document::start_or_resume_object() noexcept { - if (iter.at_root()) { - return get_object(); - } - else { - return object::resume(resume_value_iterator()); - } - } - simdjson_inline simdjson_result document::get_value() noexcept { - // Make sure we start any arrays or objects before returning, so that start_root_() - // 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 document::get_array() & noexcept { - auto value = get_root_value_iterator(); - return array::start_root(value); - } - simdjson_inline simdjson_result 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 document::get_uint64() noexcept { - return get_root_value_iterator().get_root_uint64(true); - } - simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { - return get_root_value_iterator().get_root_uint64_in_string(true); - } - simdjson_inline simdjson_result document::get_int64() noexcept { - return get_root_value_iterator().get_root_int64(true); - } - simdjson_inline simdjson_result document::get_int64_in_string() noexcept { - return get_root_value_iterator().get_root_int64_in_string(true); - } - simdjson_inline simdjson_result document::get_double() noexcept { - return get_root_value_iterator().get_root_double(true); - } - simdjson_inline simdjson_result document::get_double_in_string() noexcept { - return get_root_value_iterator().get_root_double_in_string(true); - } - simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { - return get_root_value_iterator().get_root_string(true, allow_replacement); - } - template - 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 document::get_wobbly_string() noexcept { - return get_root_value_iterator().get_root_wobbly_string(true); - } - simdjson_inline simdjson_result document::get_raw_json_string() noexcept { - return get_root_value_iterator().get_root_raw_json_string(true); - } - simdjson_inline simdjson_result document::get_bool() noexcept { - return get_root_value_iterator().get_root_bool(true); - } - simdjson_inline simdjson_result document::is_null() noexcept { - return get_root_value_iterator().is_root_null(true); - } - - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } - - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_value(); } - - template simdjson_inline error_code document::get(T& out) & noexcept { - return get().get(out); - } - template simdjson_deprecated simdjson_inline error_code document::get(T& out) && noexcept { - return std::forward(*this).get().get(out); - } - -#if SIMDJSON_EXCEPTIONS - template - simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } - template - simdjson_inline document::operator T() & noexcept(false) { return get(); } - 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) { return get_string(false); } - simdjson_inline document::operator raw_json_string() noexcept(false) { 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 document::count_elements() & noexcept { - auto a = get_array(); - simdjson_result 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 document::count_fields() & noexcept { - auto a = get_object(); - simdjson_result 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 document::at(size_t index) & noexcept { - auto a = get_array(); - return a.at(index); - } - simdjson_inline simdjson_result document::begin() & noexcept { - return get_array().begin(); - } - simdjson_inline simdjson_result document::end() & noexcept { - return {}; - } - - simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { - return start_or_resume_object().find_field(key); - } - simdjson_inline simdjson_result document::find_field(const char* key) & noexcept { - return start_or_resume_object().find_field(key); - } - simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { - return start_or_resume_object().find_field_unordered(key); - } - simdjson_inline simdjson_result document::find_field_unordered(const char* key) & noexcept { - return start_or_resume_object().find_field_unordered(key); - } - simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { - return start_or_resume_object()[key]; - } - simdjson_inline simdjson_result document::operator[](const char* key) & noexcept { - return start_or_resume_object()[key]; - } - - simdjson_inline error_code document::consume() noexcept { - auto error = iter.skip_child(0); - if (error) { iter.abandon(); } - return error; - } - - simdjson_inline simdjson_result 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(starting_point), size_t(final_point - starting_point)); - } - - simdjson_inline simdjson_result document::type() noexcept { - return get_root_value_iterator().type(); - } - - simdjson_inline simdjson_result document::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 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 document::is_integer() noexcept { - return get_root_value_iterator().is_root_integer(true); - } - - simdjson_inline simdjson_result document::get_number_type() noexcept { - return get_root_value_iterator().get_root_number_type(true); - } - - simdjson_inline simdjson_result document::get_number() noexcept { - return get_root_value_iterator().get_root_number(true); - } - - - simdjson_inline simdjson_result document::raw_json_token() noexcept { - auto _iter = get_root_value_iterator(); - return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); - } - - simdjson_inline simdjson_result 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 document::at_path(std::string_view json_path) noexcept { - rewind(); // Rewind the document each time at_pointer is called - if (json_path.empty()) { - return this->get_value(); - } - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) { - case json_type::array: - return (*this).get_array().at_path(json_path); - case json_type::object: - return (*this).get_object().at_path(json_path); - default: - return INVALID_JSON_POINTER; - } - } - - } // namespace ondemand - } // namespace arm64 -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result( - arm64::ondemand::document&& value - ) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) - { - } - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept : - implementation_simdjson_result_base( - error - ) - { - } - simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); - } - simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); - } - simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; - } - simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() & noexcept { - return {}; - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char* key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](const char* key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::find_field(const char* key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { - if (error()) { return error(); } - return first.get_array(); - } - simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { - if (error()) { return error(); } - return first.get_object(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); - } - simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); - } - simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); - } - template - simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(receiver, allow_replacement); - } - simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); - } - simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); - } - simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); - } - simdjson_inline simdjson_result simdjson_result::get_value() noexcept { - if (error()) { return error(); } - return first.get_value(); - } - simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); - } - - template - simdjson_inline simdjson_result simdjson_result::get() & noexcept { - if (error()) { return error(); } - return first.get(); - } - template - simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { - if (error()) { return error(); } - return std::forward(first).get(); - } - template - simdjson_inline error_code simdjson_result::get(T& out) & noexcept { - if (error()) { return error(); } - return first.get(out); - } - template - simdjson_inline error_code simdjson_result::get(T& out) && noexcept { - if (error()) { return error(); } - return std::forward(first).get(out); - } - - template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; - template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { - if (error()) { return error(); } - return std::forward(first); - } - template<> simdjson_inline error_code simdjson_result::get(arm64::ondemand::document& out) & noexcept = delete; - template<> simdjson_inline error_code simdjson_result::get(arm64::ondemand::document& out) && noexcept { - if (error()) { return error(); } - out = std::forward(first); - return SUCCESS; - } - - simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); - } - - simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); - } - - simdjson_inline simdjson_result simdjson_result::is_string() noexcept { - if (error()) { return error(); } - return first.is_string(); - } - - simdjson_inline bool simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); - } - - simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); - } - - simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); - } - - simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); - } - - -#if SIMDJSON_EXCEPTIONS - template ::value == false>::type> - simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator arm64::ondemand::array() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator arm64::ondemand::object() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator arm64::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator arm64::ondemand::value() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } + /** + * 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 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 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; + +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); + + /** + * 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. + */ + simdjson_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 buffer{}; + size_t position{0}; + size_t capacity{0}; + bool is_valid{true}; +}; - simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); - } - simdjson_inline bool simdjson_result::at_end() const noexcept { - if (error()) { return error(); } - return first.at_end(); - } - - - simdjson_inline int32_t simdjson_result::current_depth() const noexcept { - if (error()) { return error(); } - return first.current_depth(); - } - - simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); - } - - simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } - - simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { return error(); } - return first.at_path(json_path); - } - -} // namespace simdjson - - -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 document_reference::get_array() & noexcept { return doc->get_array(); } - simdjson_inline simdjson_result 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 document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } - simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } - simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } - simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } - simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } - simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } - simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } - template - 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 document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } - simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } - simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } - simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } - simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } -#if SIMDJSON_EXCEPTIONS - template - simdjson_inline document_reference::operator T() noexcept(false) { return get(); } - 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 document_reference::count_elements() & noexcept { return doc->count_elements(); } - simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } - simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } - simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } - simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } - simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } - simdjson_inline simdjson_result document_reference::find_field(const char* key) & noexcept { return doc->find_field(key); } - simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } - simdjson_inline simdjson_result document_reference::operator[](const char* key) & noexcept { return (*doc)[key]; } - simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } - simdjson_inline simdjson_result document_reference::find_field_unordered(const char* key) & noexcept { return doc->find_field_unordered(key); } - simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } - simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } - simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } - simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } - simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } - simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } - simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } - simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } - simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } - simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } - simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } - simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } - simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json(); } - simdjson_inline document_reference::operator document& () const noexcept { return *doc; } - - } // namespace ondemand - } // namespace arm64 -} // namespace simdjson - - - -namespace simdjson { - simdjson_inline simdjson_result::simdjson_result(arm64::ondemand::document_reference value, error_code error) - noexcept : implementation_simdjson_result_base(std::forward(value), error) {} - - - simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); - } - simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); - } - simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; - } - simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() & noexcept { - return {}; - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char* key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](const char* key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::find_field(const char* key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { - if (error()) { return error(); } - return first.get_array(); - } - simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { - if (error()) { return error(); } - return first.get_object(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); - } - simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); - } - simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); - } - template - simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(receiver, allow_replacement); - } - simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); - } - simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); - } - simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); - } - simdjson_inline simdjson_result simdjson_result::get_value() noexcept { - if (error()) { return error(); } - return first.get_value(); - } - simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); - } - simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); - } - simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); - } - simdjson_inline simdjson_result simdjson_result::is_string() noexcept { - if (error()) { return error(); } - return first.is_string(); - } - simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); - } - simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); - } - simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); - } - simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); - } -#if SIMDJSON_EXCEPTIONS - template ::value == false>::type> - simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator arm64::ondemand::array() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator arm64::ondemand::object() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator arm64::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator arm64::ondemand::value() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } -#endif - - simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); - } - - simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); - } - - simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } - - simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { - return error(); - } - return first.at_path(json_path); - } - -} // namespace simdjson - -#endif // SIMDJSON_GENERIC_ONDEMAND_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 -#include - -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 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 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 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 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 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 - ) 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 }, - 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 }, - allow_comma_separated{ false }, - 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 { - 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 document_stream::iterator::operator*() noexcept { - //if(stream->error) { return stream->error; } - return simdjson_result(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::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; } - doc_index = batch_start; - 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 - 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(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; - } - } - } - - inline void document_stream::next_document() noexcept { - // 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) { doc.iter.consume_character(','); } - // 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; - if (remaining <= batch_size) { - return p.implementation->stage1(&buf[_batch_start], remaining, stage1_mode::streaming_final); - } - else { - return p.implementation->stage1(&buf[_batch_start], batch_size, stage1_mode::streaming_partial); - } - } - - 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 { - auto depth = stream->doc.iter.depth(); - auto cur_struct_index = stream->doc.iter._root - stream->parser->implementation->structural_indexes.get(); - - // If at root, process the first token to determine if scalar value - if (stream->doc.iter.at_root()) { - 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 - // TODO: We could remove trailing whitespaces - // This returns a string spanning from start of value to the beginning of the next document (excluded) - { - auto next_index = 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(stream->buf) + current_index(); - while (svlen > 1 && (std::isspace(start[svlen - 1]) || start[svlen - 1] == '\0')) { - svlen--; - } - return std::string_view(start, svlen); - } - } - cur_struct_index++; - } - - while (cur_struct_index <= static_cast(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(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::simdjson_result( - error_code error - ) noexcept : - implementation_simdjson_result_base(error) - { - } - simdjson_inline simdjson_result::simdjson_result( - arm64::ondemand::document_stream&& value - ) noexcept : - implementation_simdjson_result_base( - std::forward(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 + +#if !SIMDJSON_STATIC_REFLECTION +// fallback implementation until we have static reflection +template +simdjson_warn_unused simdjson_result 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 +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_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): #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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ +#if SIMDJSON_STATIC_REFLECTION -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() {} - - simdjson_inline field::field(raw_json_string key, ondemand::value&& value) noexcept - : std::pair(key, std::forward(value)) - { - } - - simdjson_inline simdjson_result 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::start(const value_iterator& parent_iter, raw_json_string key) noexcept { - return field(key, parent_iter.child()); - } - - simdjson_inline simdjson_warn_unused simdjson_result 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 answer = first.unescape(second.iter.json_iter(), allow_replacement); - first.consume(); - return answer; - } - - template - 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)); - 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(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(first.buf), end_quote - first.buf); - } - - simdjson_inline value& field::value() & noexcept { - return second; - } - - simdjson_inline value field::value() && noexcept { - return std::forward(*this).second; - } - - } // namespace ondemand - } // namespace arm64 -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result( - arm64::ondemand::field&& value - ) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) - { - } - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept : - implementation_simdjson_result_base(error) - { - } - - simdjson_inline simdjson_result simdjson_result::key() noexcept { - if (error()) { return error(); } - return first.key(); - } - - simdjson_inline simdjson_result simdjson_result::key_raw_json_token() noexcept { - if (error()) { return error(); } - return first.key_raw_json_token(); - } - - simdjson_inline simdjson_result simdjson_result::escaped_key() noexcept { - if (error()) { return error(); } - return first.escaped_key(); - } - - simdjson_inline simdjson_result simdjson_result::unescaped_key(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.unescaped_key(allow_replacement); - } - - template - simdjson_inline error_code simdjson_result::unescaped_key(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.unescaped_key(receiver, allow_replacement); - } - - simdjson_inline simdjson_result simdjson_result::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(other.token)), - parser{ other.parser }, - _string_buf_loc{ other._string_buf_loc }, - error{ other.error }, - _depth{ other._depth }, - _root{ other._root }, - _streaming{ other._streaming } - { - 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; - 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 } - - { - 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 '"': - if (*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(TAPE_ERROR, "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; - } - - 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 { -#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(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 json_iterator::current_location() const noexcept { - if (!is_alive()) { // Unrecoverable error - if (!at_root()) { - return reinterpret_cast(token.peek(-1)); - } - else { - return reinterpret_cast(token.peek()); - } - } - if (at_end()) { - return OUT_OF_BOUNDS; - } - return reinterpret_cast(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_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 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); - SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc)); - return result; -#else - return parser->unescape(in, _string_buf_loc, allow_replacement); -#endif - } - - simdjson_inline simdjson_result json_iterator::unescape_wobbly(raw_json_string in) noexcept { -#if SIMDJSON_DEVELOPMENT_CHECKS - auto result = parser->unescape_wobbly(in, _string_buf_loc); - SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc)); - 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_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_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::simdjson_result(arm64::ondemand::json_iterator&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(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; - default: SIMDJSON_UNREACHABLE(); - } - return out; - } - -#if SIMDJSON_EXCEPTIONS - inline std::ostream& operator<<(std::ostream& out, simdjson_result& 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::simdjson_result(arm64::ondemand::json_type&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(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 +#include #include +#include +#include +#include +#include +#include +#include +// #include // for std::define_static_string - header not available yet namespace simdjson { - namespace arm64 { - namespace ondemand { - namespace logger { +namespace arm64 { +namespace builder { - 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. +template + requires(concepts::container_but_not_string && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + auto it = t.begin(); + auto end = t.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} - // Helper to turn unprintable or newline characters into spaces - static inline char printable_char(char c) { - if (c >= 0x20) { - return c; - } - else { - return ' '; - } - } +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +constexpr void atom(string_builder &b, const T &t) { + b.escape_and_append_with_quotes(t); +} - template - 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_s); - if (size <= 0) return std::string(); - std::unique_ptr 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 - 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)...); - } - - template - 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)...); - 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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -namespace simdjson { - namespace arm64 { - namespace ondemand { - - simdjson_inline simdjson_result 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(key.size()), key.data()); - return NO_SUCH_FIELD; - } - return value(iter.child()); - } - simdjson_inline simdjson_result 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(key.size()), key.data()); - return NO_SUCH_FIELD; - } - return value(iter.child()); - } - simdjson_inline simdjson_result object::operator[](const std::string_view key) & noexcept { - return find_field_unordered(key); - } - simdjson_inline simdjson_result object::operator[](const std::string_view key) && noexcept { - return std::forward(*this).find_field_unordered(key); - } - simdjson_inline simdjson_result object::find_field(const std::string_view key) & noexcept { - 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(key.size()), key.data()); - return NO_SUCH_FIELD; - } - return value(iter.child()); - } - simdjson_inline simdjson_result object::find_field(const std::string_view key) && noexcept { - 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(key.size()), key.data()); - return NO_SUCH_FIELD; - } - return value(iter.child()); - } - - simdjson_inline simdjson_result object::start(value_iterator& iter) noexcept { - SIMDJSON_TRY(iter.start_object().error()); - return object(iter); - } - simdjson_inline simdjson_result object::start_root(value_iterator& iter) noexcept { - SIMDJSON_TRY(iter.start_root_object().error()); - return object(iter); - } - 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 object::raw_json() noexcept { - 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(starting_point), size_t(final_point - starting_point)); - } - - simdjson_inline simdjson_result 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::begin() noexcept { -#if SIMDJSON_DEVELOPMENT_CHECKS - if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } -#endif - return object_iterator(iter); - } - simdjson_inline simdjson_result object::end() noexcept { - return object_iterator(iter); - } - - inline simdjson_result object::at_pointer(std::string_view json_pointer) noexcept { - if (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 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 object::at_path(std::string_view json_path) noexcept { - auto json_pointer = json_path_to_pointer_conversion(json_path); - if (json_pointer == "-1") { - return INVALID_JSON_POINTER; - } - return at_pointer(json_pointer); - } - - simdjson_inline simdjson_result object::count_fields() & noexcept { - size_t count{ 0 }; - // Important: we do not consume any of the values. - 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 object::is_empty() & noexcept { - 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 object::reset() & noexcept { - return iter.reset_object(); - } - - } // namespace ondemand - } // namespace arm64 -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result(arm64::ondemand::object&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} - - simdjson_inline simdjson_result simdjson_result::begin() noexcept { - if (error()) { return error(); } - return first.begin(); +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &m) { + if (m.empty()) { + b.append_raw("{}"); + return; + } + b.append('{'); + bool first = true; + for (const auto& [key, value] : m) { + if (!first) { + b.append(','); } - simdjson_inline simdjson_result simdjson_result::end() noexcept { - if (error()) { return error(); } - return first.end(); + first = false; + // Keys must be convertible to string_view per the concept + b.escape_and_append_with_quotes(key); + b.append(':'); + atom(b, value); + } + b.append('}'); +} + + +template::value && !std::is_same_v>::type> +constexpr void atom(string_builder &b, const number_type t) { + b.append(t); +} + +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, t.[:dm:]); + i++; + }; + b.append('}'); +} + +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &opt) { + if (opt) { + atom(b, opt.value()); + } else { + b.append_raw("null"); + } +} + +// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &ptr) { + if (ptr) { + atom(b, *ptr); + } else { + b.append_raw("null"); + } +} + +// Support for enums - serialize as string representation using expand approach from P2996R12 +template + requires(std::is_enum_v && !require_custom_serialization) +void atom(string_builder &b, const T &e) { +#if SIMDJSON_STATIC_REFLECTION + 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(std::meta::identifier_of(enum_val))); + if (e == [:enum_val:]) { + b.append_raw(enum_str); + return; } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) && noexcept { - if (error()) { return error(); } - return std::forward(first).find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) && noexcept { - if (error()) { return error(); } - return std::forward(first)[key]; - } - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) && noexcept { - if (error()) { return error(); } - return std::forward(first).find_field(key); - } - - simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } - - simdjson_inline simdjson_result simdjson_result::at_path( - std::string_view json_path) noexcept { - if (error()) { - return error(); - } - return first.at_path(json_path); - } - - inline simdjson_result simdjson_result::reset() noexcept { - if (error()) { return error(); } - return first.reset(); - } - - inline simdjson_result simdjson_result::is_empty() noexcept { - if (error()) { return error(); } - return first.is_empty(); - } - - simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); - } - - simdjson_inline simdjson_result simdjson_result::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 } - {} - - simdjson_inline simdjson_result object_iterator::operator*() noexcept { - 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 { - // 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::simdjson_result( - arm64::ondemand::object_iterator&& value - ) noexcept - : implementation_simdjson_result_base(std::forward(value)) - { - first.iter.assert_is_valid(); - } - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base({}, error) - { - } - - simdjson_inline simdjson_result simdjson_result::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::operator==(const simdjson_result& 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::operator!=(const simdjson_result& other) const noexcept { - if (!first.iter.is_valid()) { return error(); } - return first != other.first; - } - // Checks for ']' and ',' - simdjson_inline simdjson_result& simdjson_result::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/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; - 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 parser::iterate(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } - - json.remove_utf8_bom(); - - // 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(json.data()), json.length(), stage1_mode::regular)); - return document::start({ reinterpret_cast(json.data()), this }); - } - -#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON - simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } - - json.remove_utf8_bom(); - - // 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(json.data()), json.length(), stage1_mode::regular); - if (err) { - if (err != UNCLOSED_STRING) - return err; - } - return document::start({ reinterpret_cast(json.data()), this, true }); - } -#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON - - simdjson_warn_unused simdjson_inline simdjson_result 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 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 parser::iterate(std::string_view json, size_t allocated) & noexcept { - return iterate(padded_string_view(json, allocated)); - } - - simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(std::string& json) & noexcept { - if (json.capacity() - json.size() < SIMDJSON_PADDING) { - json.reserve(json.size() + SIMDJSON_PADDING); - } - return iterate(padded_string_view(json)); - } - - simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const std::string& json) & noexcept { - return iterate(padded_string_view(json)); - } - - simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const simdjson_result& result) & noexcept { - // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 parser::iterate(const simdjson_result& result) & noexcept { - // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 parser::iterate_raw(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_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(json.data()), json.length(), stage1_mode::regular)); - return json_iterator(reinterpret_cast(json.data()), this); - } - - inline simdjson_result parser::iterate_many(const uint8_t* buf, size_t len, size_t batch_size, bool allow_comma_separated) 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 (allow_comma_separated && batch_size < len) { batch_size = len; } - return document_stream(*this, buf, len, batch_size, allow_comma_separated); - } - inline simdjson_result parser::iterate_many(const char* buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept { - return iterate_many(reinterpret_cast(buf), len, batch_size, allow_comma_separated); - } - inline simdjson_result parser::iterate_many(const std::string& s, size_t batch_size, bool allow_comma_separated) noexcept { - return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated); - } - inline simdjson_result parser::iterate_many(const padded_string& s, size_t batch_size, bool allow_comma_separated) noexcept { - return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated); - } - - 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 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(dst), end - dst); - dst = end; - return result; - } - - simdjson_inline simdjson_warn_unused simdjson_result 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(dst), end - dst); - dst = end; - return result; - } - - } // namespace ondemand - } // namespace arm64 -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result(arm64::ondemand::parser&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(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(buf); } - - - simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept { - size_t pos{ 0 }; - // if the content has no escape character, just scan through it quickly! - for (; pos < target.size() && target[pos] != '\\'; pos++) {} - // slow path may begin. - bool escaping{ false }; - for (; pos < target.size(); pos++) { - if ((target[pos] == '"') && !escaping) { - return false; - } - else if (target[pos] == '\\') { - escaping = !escaping; - } - else { - escaping = false; - } - } - return true; - } - - simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* target) noexcept { - size_t pos{ 0 }; - // if the content has no escape character, just scan through it quickly! - for (; target[pos] && target[pos] != '\\'; pos++) {} - // slow path may begin. - bool escaping{ false }; - for (; target[pos]; pos++) { - if ((target[pos] == '"') && !escaping) { - return false; - } - else if (target[pos] == '\\') { - escaping = !escaping; - } - else { - escaping = false; - } - } - 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()); - } - const char* r{ raw() }; - size_t pos{ 0 }; - for (; pos < target.size(); pos++) { - if (r[pos] != target[pos]) { return false; } - } - if (r[pos] != '"') { return false; } - return true; - } - - 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 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 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::simdjson_result(arm64::ondemand::raw_json_string&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} - - simdjson_inline simdjson_result simdjson_result::raw() const noexcept { - if (error()) { return error(); } - return first.raw(); - } - simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::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 simdjson_result::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/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): #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 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 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 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 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 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 to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); - } - - inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); - } - - inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); - } - - inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); - } - - inline simdjson_result to_json_string(simdjson_result 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 x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); - } + }; + // Fallback to integer if enum value not found + atom(b, static_cast>(e)); #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); - } - } + // Fallback: serialize as integer if reflection not available + atom(b, static_cast>(e)); #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 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&& x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); - } - inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& 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 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 - } +// Support for appendable containers that don't have operator[] (sets, etc.) +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &container) { + if (container.empty()) { + b.append_raw("[]"); + return; + } + b.append('['); + bool first = true; + for (const auto& item : container) { + if (!first) { + b.append(','); } -} // namespace simdjson::arm64::ondemand + first = false; + atom(b, item); + } + b.append(']'); +} -#endif // SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H -/* end file simdjson/generic/ondemand/serialization-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 +// append functions that delegate to atom functions for primitive types +template + requires(std::is_arithmetic_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +// works for struct +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^Z, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, z.[:dm:]); + i++; + }; + b.append('}'); +} + +// works for container that have begin() and end() iterators +template + requires(concepts::container_but_not_string && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + auto it = z.begin(); + auto end = z.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} + +template + requires (require_custom_serialization) +void append(string_builder &b, const Z &z) { + b.append(z); +} + + +template +simdjson_warn_unused simdjson_result to_json_string(const Z &z, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +template +simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + 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 +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 + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +void extract_from(string_builder &b, const T &obj) { + // 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) || ...); + }; + + b.append('{'); + bool first = true; + + // 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_public(mem)) { + 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 (should_extract(key)) { + if (!first) { + b.append(','); + } + first = false; + + // Serialize the key + constexpr auto quoted_key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem))); + b.append_raw(quoted_key); + b.append(':'); + + // Serialize the value + atom(b, obj.[:mem:]); + } + } + }; + + b.append('}'); +} + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + extract_from(b, obj); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +} // namespace builder +} // namespace arm64 +// Alias the function template to 'to' in the global namespace +template +simdjson_warn_unused simdjson_result to_json(const Z &z, size_t initial_capacity = arm64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + arm64::builder::string_builder b(initial_capacity); + arm64::builder::append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} +template +simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = arm64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + arm64::builder::string_builder b(initial_capacity); + arm64::builder::append(b, z); + std::string_view view; + if(auto e = b.view().get(view); e) { return e; } + s.assign(view); + return SUCCESS; +} +// Global namespace function for extract_from +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result 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(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): #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): #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 */ -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 +#if SIMDJSON_STATIC_REFLECTION namespace simdjson { +namespace arm64 { +namespace builder { - simdjson_inline simdjson_result::simdjson_result(arm64::ondemand::token_iterator&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} +/** + * 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 +simdjson_warn_unused simdjson_result 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 + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result 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(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 +simdjson_warn_unused simdjson_result 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 + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result 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(obj, opts, initial_capacity); +} } // 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-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 +#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 +#include +#include +#ifndef SIMDJSON_GENERIC_STRING_BUILDER_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): #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 SIMDJSON_EXPERIMENTAL_HAS_NEON +#include +#ifdef _MSC_VER +#include +#endif +#endif +#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 +#include +#ifdef _MSC_VER +#include +#endif +#endif + namespace simdjson { - namespace arm64 { - namespace ondemand { +namespace arm64 { +namespace builder { - 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; - } +static SIMDJSON_CONSTEXPR_LAMBDA std::array + 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}; - simdjson_inline simdjson_result value::get_array() noexcept { - return array::start(iter); - } - simdjson_inline simdjson_result value::get_object() noexcept { - return object::start(iter); - } - simdjson_inline simdjson_result value::start_or_resume_object() noexcept { - if (iter.at_start()) { - return get_object(); - } - else { - return object::resume(iter); - } - } +/** - simdjson_inline simdjson_result value::get_raw_json_string() noexcept { - return iter.get_raw_json_string(); - } - simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { - return iter.get_string(allow_replacement); - } - template - 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 value::get_wobbly_string() noexcept { - return iter.get_wobbly_string(); - } - simdjson_inline simdjson_result value::get_double() noexcept { - return iter.get_double(); - } - simdjson_inline simdjson_result value::get_double_in_string() noexcept { - return iter.get_double_in_string(); - } - simdjson_inline simdjson_result value::get_uint64() noexcept { - return iter.get_uint64(); - } - simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { - return iter.get_uint64_in_string(); - } - simdjson_inline simdjson_result value::get_int64() noexcept { - return iter.get_int64(); - } - simdjson_inline simdjson_result value::get_int64_in_string() noexcept { - return iter.get_int64_in_string(); - } - simdjson_inline simdjson_result value::get_bool() noexcept { - return iter.get_bool(); - } - simdjson_inline simdjson_result value::is_null() noexcept { - return iter.is_null(); - } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } +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). - template simdjson_inline error_code value::get(T& out) noexcept { - return get().get(out); - } +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; +} + +**/ + +SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline bool +simple_needs_escaping(std::string_view v) { + for (char c : v) { + // a table lookup is faster than a series of comparisons + if (json_quotable_character[static_cast(c)]) { + return true; + } + } + return false; +} + +#if SIMDJSON_EXPERIMENTAL_HAS_NEON +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + uint8x16_t running = vdupq_n_u8(0); + uint8x16_t v34 = vdupq_n_u8(34); + uint8x16_t v92 = vdupq_n_u8(92); + + for (; i + 15 < view.size(); i += 16) { + uint8x16_t word = vld1q_u8((const uint8_t *)view.data() + i); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + if (i < view.size()) { + uint8x16_t word = + vld1q_u8((const uint8_t *)view.data() + view.length() - 16); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + return vmaxvq_u32(vreinterpretq_u32_u8(running)) != 0; +} +#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2 +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + __m128i running = _mm_setzero_si128(); + for (; i + 15 < view.size(); i += 16) { + + __m128i word = + _mm_loadu_si128(reinterpret_cast(view.data() + i)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + if (i < view.size()) { + __m128i word = _mm_loadu_si128( + reinterpret_cast(view.data() + view.length() - 16)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + return _mm_movemask_epi8(running) != 0; +} +#else +simdjson_inline bool fast_needs_escaping(std::string_view view) { + return simple_needs_escaping(view); +} +#endif + +// 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(*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(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)); + + if (vmaxvq_u32(vreinterpretq_u32_u8(needs_escape)) != 0) { + // Found quotable character - extract exact byte position using ctz + uint64x2_t as64 = vreinterpretq_u64_u8(needs_escape); + uint64_t lo = vgetq_lane_u64(as64, 0); + uint64_t hi = vgetq_lane_u64(as64, 1); + size_t offset = ptr - reinterpret_cast(view.data()); +#ifdef _MSC_VER + unsigned long trailing_zero = 0; + if (lo != 0) { + _BitScanForward64(&trailing_zero, lo); + return offset + trailing_zero / 8; + } else { + _BitScanForward64(&trailing_zero, hi); + return offset + 8 + trailing_zero / 8; + } +#else + if (lo != 0) { + return offset + __builtin_ctzll(lo) / 8; + } else { + return offset + 8 + __builtin_ctzll(hi) / 8; + } +#endif + } + 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(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(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(view.data()); +#ifdef _MSC_VER + unsigned long trailing_zero = 0; + _BitScanForward(&trailing_zero, mask); + return offset + trailing_zero; +#else + return offset + __builtin_ctz(mask); +#endif + } + 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(); + } +} + +// 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 out - initout; +} + +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; +} + +simdjson_inline void string_builder::grow_buffer(size_t desired_capacity) { + if (!is_valid) { + return; + } + std::unique_ptr 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 { + +template ::value>::type> +simdjson_really_inline int int_log2(number_type x) { + return 63 - leading_zeroes(uint64_t(x) | 1); +} + +simdjson_really_inline int fast_digit_count_32(uint32_t x) { + static uint64_t table[] = { + 4294967296, 8589934582, 8589934582, 8589934582, 12884901788, + 12884901788, 12884901788, 17179868184, 17179868184, 17179868184, + 21474826480, 21474826480, 21474826480, 21474826480, 25769703776, + 25769703776, 25769703776, 30063771072, 30063771072, 30063771072, + 34349738368, 34349738368, 34349738368, 34349738368, 38554705664, + 38554705664, 38554705664, 41949672960, 41949672960, 41949672960, + 42949672960, 42949672960}; + return uint32_t((x + table[int_log2(x)]) >> 32); +} + +simdjson_really_inline int fast_digit_count_64(uint64_t x) { + static uint64_t table[] = {9, + 99, + 999, + 9999, + 99999, + 999999, + 9999999, + 99999999, + 999999999, + 9999999999, + 99999999999, + 999999999999, + 9999999999999, + 99999999999999, + 999999999999999ULL, + 9999999999999999ULL, + 99999999999999999ULL, + 999999999999999999ULL, + 9999999999999999999ULL}; + int y = (19 * int_log2(x) >> 6); + y += x > table[y]; + return y + 1; +} + +template ::value>::type> +simdjson_really_inline size_t digit_count(number_type v) noexcept { + static_assert(sizeof(number_type) == 8 || sizeof(number_type) == 4 || + sizeof(number_type) == 2 || sizeof(number_type) == 1, + "We only support 8-bit, 16-bit, 32-bit and 64-bit numbers"); + SIMDJSON_IF_CONSTEXPR(sizeof(number_type) <= 4) { + return fast_digit_count_32(static_cast(v)); + } + else { + return fast_digit_count_64(static_cast(v)); + } +} +static const char decimal_table[200] = { + 0x30, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35, + 0x30, 0x36, 0x30, 0x37, 0x30, 0x38, 0x30, 0x39, 0x31, 0x30, 0x31, 0x31, + 0x31, 0x32, 0x31, 0x33, 0x31, 0x34, 0x31, 0x35, 0x31, 0x36, 0x31, 0x37, + 0x31, 0x38, 0x31, 0x39, 0x32, 0x30, 0x32, 0x31, 0x32, 0x32, 0x32, 0x33, + 0x32, 0x34, 0x32, 0x35, 0x32, 0x36, 0x32, 0x37, 0x32, 0x38, 0x32, 0x39, + 0x33, 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x33, 0x34, 0x33, 0x35, + 0x33, 0x36, 0x33, 0x37, 0x33, 0x38, 0x33, 0x39, 0x34, 0x30, 0x34, 0x31, + 0x34, 0x32, 0x34, 0x33, 0x34, 0x34, 0x34, 0x35, 0x34, 0x36, 0x34, 0x37, + 0x34, 0x38, 0x34, 0x39, 0x35, 0x30, 0x35, 0x31, 0x35, 0x32, 0x35, 0x33, + 0x35, 0x34, 0x35, 0x35, 0x35, 0x36, 0x35, 0x37, 0x35, 0x38, 0x35, 0x39, + 0x36, 0x30, 0x36, 0x31, 0x36, 0x32, 0x36, 0x33, 0x36, 0x34, 0x36, 0x35, + 0x36, 0x36, 0x36, 0x37, 0x36, 0x38, 0x36, 0x39, 0x37, 0x30, 0x37, 0x31, + 0x37, 0x32, 0x37, 0x33, 0x37, 0x34, 0x37, 0x35, 0x37, 0x36, 0x37, 0x37, + 0x37, 0x38, 0x37, 0x39, 0x38, 0x30, 0x38, 0x31, 0x38, 0x32, 0x38, 0x33, + 0x38, 0x34, 0x38, 0x35, 0x38, 0x36, 0x38, 0x37, 0x38, 0x38, 0x38, 0x39, + 0x39, 0x30, 0x39, 0x31, 0x39, 0x32, 0x39, 0x33, 0x39, 0x34, 0x39, 0x35, + 0x39, 0x36, 0x39, 0x37, 0x39, 0x38, 0x39, 0x39, +}; +} // namespace internal + +template +simdjson_inline void string_builder::append(number_type v) noexcept { + static_assert(std::is_same::value || + std::is_integral::value || + std::is_floating_point::value, + "Unsupported number type"); + // If C++17 is available, we can 'if constexpr' here. + SIMDJSON_IF_CONSTEXPR(std::is_same::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::value) { + // Process 4 digits at a time instead of 2, reducing store operations + // and divisions by approximately half for large numbers. + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + unsigned_type pv = static_cast(v); + size_t dc = internal::digit_count(pv); + char *write_pointer = buffer.get() + position + dc - 1; + + // Process 4 digits per iteration for large numbers + while (pv >= 10000) { + unsigned_type q = pv / 10000; + unsigned_type r = pv % 10000; + unsigned_type r_hi = r / 100; // High 2 digits of remainder + unsigned_type r_lo = r % 100; // Low 2 digits of remainder + // Write low 2 digits first (rightmost), then high 2 digits + memcpy(write_pointer - 1, &internal::decimal_table[r_lo * 2], 2); + memcpy(write_pointer - 3, &internal::decimal_table[r_hi * 2], 2); + write_pointer -= 4; + pv = q; + } + + // Handle remaining 1-4 digits with original 2-digit loop + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_integral::value) { + // Same 4-digit batching as unsigned path for signed integers + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + bool negative = v < 0; + unsigned_type pv = static_cast(v); + if (negative) { + pv = 0 - pv; // the 0 is for Microsoft + } + size_t dc = internal::digit_count(pv); + // by always writing the minus sign, we avoid the branch. + buffer.get()[position] = '-'; + position += negative ? 1 : 0; + char *write_pointer = buffer.get() + position + dc - 1; + + // Process 4 digits per iteration for large numbers + while (pv >= 10000) { + unsigned_type q = pv / 10000; + unsigned_type r = pv % 10000; + unsigned_type r_hi = r / 100; + unsigned_type r_lo = r % 100; + memcpy(write_pointer - 1, &internal::decimal_table[r_lo * 2], 2); + memcpy(write_pointer - 3, &internal::decimal_table[r_hi * 2], 2); + write_pointer -= 4; + pv = q; + } + + // Handle remaining 1-4 digits + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_floating_point::value) { + constexpr size_t max_number_size = 24; + if (capacity_check(max_number_size)) { + // 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. + 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. + 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 +simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept { + escape_and_append_with_quotes(constevalutil::string_constant::value); +} +#endif + +simdjson_inline void string_builder::append_raw(const char *c) noexcept { + size_t len = std::strlen(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; + } +} +#if SIMDJSON_SUPPORTS_CONCEPTS +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +simdjson_inline void string_builder::append(const T &opt) { + if (opt) { + append(*opt); + } else { + append_null(); + } +} + +template + requires(require_custom_serialization) +simdjson_inline void string_builder::append(T &&val) { + serialize(*this, std::forward(val)); +} + +template + requires(std::is_convertible::value || + std::is_same::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 + requires(!std::is_convertible::value && !require_custom_serialization) +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>) { + 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 - template - simdjson_inline value::operator T() noexcept(false) { - return get(); - } - 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(); - } +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 value::begin() & noexcept { - return get_array().begin(); - } - simdjson_inline simdjson_result value::end() & noexcept { - return {}; - } - simdjson_inline simdjson_result value::count_elements() & noexcept { - simdjson_result 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 value::count_fields() & noexcept { - simdjson_result answer; - auto a = get_object(); - answer = a.count_fields(); - iter.move_at_start(); - return answer; - } - simdjson_inline simdjson_result value::at(size_t index) noexcept { - auto a = get_array(); - return a.at(index); - } +simdjson_inline simdjson_result +string_builder::view() const noexcept { + if (!is_valid) { + return simdjson::OUT_OF_CAPACITY; + } + return std::string_view(buffer.get(), position); +} - simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { - return start_or_resume_object().find_field(key); - } - simdjson_inline simdjson_result value::find_field(const char* key) noexcept { - return start_or_resume_object().find_field(key); - } +simdjson_inline simdjson_result string_builder::c_str() noexcept { + if (capacity_check(1)) { + buffer.get()[position] = '\0'; + return buffer.get(); + } + return simdjson::OUT_OF_CAPACITY; +} - simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { - return start_or_resume_object().find_field_unordered(key); - } - simdjson_inline simdjson_result value::find_field_unordered(const char* key) noexcept { - return start_or_resume_object().find_field_unordered(key); - } +simdjson_inline bool string_builder::validate_unicode() const noexcept { + return simdjson::validate_utf8(buffer.get(), position); +} - simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { - return start_or_resume_object()[key]; - } - simdjson_inline simdjson_result value::operator[](const char* key) noexcept { - return start_or_resume_object()[key]; - } +simdjson_inline void string_builder::start_object() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '{'; + } +} - simdjson_inline simdjson_result value::type() noexcept { - return iter.type(); - } +simdjson_inline void string_builder::end_object() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '}'; + } +} - simdjson_inline simdjson_result 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 void string_builder::start_array() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '['; + } +} - simdjson_inline simdjson_result value::is_string() noexcept { - json_type this_type; - auto error = type().get(this_type); - if (error) { return error; } - return (this_type == json_type::string); - } +simdjson_inline 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 bool value::is_negative() noexcept { - return iter.is_negative(); - } +simdjson_inline void string_builder::append_colon() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = ':'; + } +} - simdjson_inline simdjson_result value::is_integer() noexcept { - return iter.is_integer(); - } - simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { - return iter.get_number_type(); - } - simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { - return iter.get_number(); - } +template +simdjson_inline void +string_builder::append_key_value(key_type key, value_type value) noexcept { + static_assert(std::is_same::value || + std::is_convertible::value, + "Unsupported key type"); + escape_and_append_with_quotes(key); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(value); + } +} - simdjson_inline std::string_view value::raw_json_token() noexcept { - return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); - } +#if SIMDJSON_SUPPORTS_CONCEPTS +template +simdjson_inline void +string_builder::append_key_value(value_type value) noexcept { + escape_and_append_with_quotes(); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(value); + } +} +#endif - simdjson_inline simdjson_result 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 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::at_pointer(std::string_view json_pointer) noexcept { - 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::at_path(std::string_view json_path) noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) { - case json_type::array: - return (*this).get_array().at_path(json_path); - case json_type::object: - return (*this).get_object().at_path(json_path); - default: - return INVALID_JSON_POINTER; - } - } - - } // namespace ondemand - } // namespace arm64 +} // namespace builder +} // namespace arm64 } // namespace simdjson -namespace simdjson { +#endif // SIMDJSON_GENERIC_STRING_BUILDER_INL_H +/* end file simdjson/generic/builder/json_string_builder-inl.h for arm64 */ - simdjson_inline simdjson_result::simdjson_result( - arm64::ondemand::value&& value - ) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) - { - } - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept : - implementation_simdjson_result_base(error) - { - } - simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); - } - simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); - } - simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() & noexcept { - if (error()) { return error(); } - return {}; - } - - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::find_field(const char* key) noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char* key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](const char* key) noexcept { - if (error()) { return error(); } - return first[key]; - } - - simdjson_inline simdjson_result simdjson_result::get_array() noexcept { - if (error()) { return error(); } - return first.get_array(); - } - simdjson_inline simdjson_result simdjson_result::get_object() noexcept { - if (error()) { return error(); } - return first.get_object(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); - } - simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); - } - simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); - } - template - simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(receiver, allow_replacement); - } - simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); - } - simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); - } - simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); - } - simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); - } - - template simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return first.get(); - } - template simdjson_inline error_code simdjson_result::get(T& out) noexcept { - if (error()) { return error(); } - return first.get(out); - } - - template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return std::move(first); - } - template<> simdjson_inline error_code simdjson_result::get(arm64::ondemand::value& out) noexcept { - if (error()) { return error(); } - out = first; - return SUCCESS; - } - - simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); - } - simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); - } - simdjson_inline simdjson_result simdjson_result::is_string() noexcept { - if (error()) { return error(); } - return first.is_string(); - } - simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); - } - simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); - } - simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); - } - simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); - } -#if SIMDJSON_EXCEPTIONS - template - simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return static_cast(first); - } - simdjson_inline simdjson_result::operator arm64::ondemand::array() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator arm64::ondemand::object() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator arm64::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } -#endif - - simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); - } - - simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { - if (error()) { return error(); } - return first.raw_json(); - } - - simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); - } - - simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { - if (error()) { return error(); } - return first.current_depth(); - } - - simdjson_inline simdjson_result simdjson_result::at_pointer( - std::string_view json_pointer) noexcept { - if (error()) { - return error(); - } - return first.at_pointer(json_pointer); - } - - simdjson_inline simdjson_result simdjson_result::at_path( - std::string_view json_path) noexcept { - if (error()) { - return error(); - } - return first.at_path(json_path); - } - -} // namespace simdjson - -#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H -/* end file simdjson/generic/ondemand/value-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 { - - 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 value_iterator::start_object() noexcept { - SIMDJSON_TRY(start_container('{', "Not an object", "object")); - return started_object(); - } - - simdjson_warn_unused simdjson_inline simdjson_result 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 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(); - 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 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 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 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 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 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_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 value_iterator::start_array() noexcept { - SIMDJSON_TRY(start_container('[', "Not an array", "array")); - return started_array(); - } - - simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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(!not_true); - } - simdjson_warn_unused simdjson_inline simdjson_result 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 value_iterator::get_string(bool allow_replacement) noexcept { - return get_raw_json_string().unescape(json_iter(), allow_replacement); - } - template - simdjson_warn_unused simdjson_inline error_code value_iterator::get_string(string_type& receiver, bool allow_replacement) noexcept { - std::string_view content; - auto err = get_string(allow_replacement).get(content); - if (err) { return err; } - receiver = content; - return SUCCESS; - } - simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_wobbly_string() noexcept { - return get_raw_json_string().unescape_wobbly(json_iter()); - } - simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_raw_json_string() noexcept { - auto json = peek_scalar("string"); - if (*json != '"') { return incorrect_type_error("Not a string"); } - advance_scalar("string"); - return raw_json_string(json + 1); - } - simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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 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 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 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 value_iterator::is_integer() noexcept { - return numberparsing::is_integer(peek_non_root_scalar("integer")); - } - simdjson_inline simdjson_result value_iterator::get_number_type() noexcept { - return numberparsing::get_number_type(peek_non_root_scalar("integer")); - } - simdjson_inline simdjson_result 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 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 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.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 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.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 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 - 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; - auto err = get_root_string(check_trailing, allow_replacement).get(content); - if (err) { return err; } - receiver = content; - return SUCCESS; - } - simdjson_warn_unused simdjson_inline simdjson_result 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 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; } - advance_scalar("string"); - return raw_json_string(json + 1); - } - simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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.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 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.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 value_iterator::get_root_bool(bool check_trailing) noexcept { - auto max_len = peek_root_length(); - auto json = peek_root_scalar("bool"); - uint8_t tmpbuf[5 + 1 + 1]; // +1 for null termination - tmpbuf[5 + 1] = '\0'; // make sure that buffer is always null terminated. - if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 5 + 1)) { return incorrect_type_error("Not a boolean"); } - auto result = parse_bool(tmpbuf); - if (result.error() == SUCCESS) { - if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; } - advance_root_scalar("bool"); - } - return result; - } - simdjson_inline simdjson_result 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"); - } - 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_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. - 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 simdjson_result value_iterator::reset_array() noexcept { - if (error()) { return error(); } - move_at_container_start(); - return started_array(); - } - - simdjson_inline simdjson_result 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 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 TAPE_ERROR; - } - } - - 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::simdjson_result(arm64::ondemand::value_iterator&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} - -} // namespace simdjson - -#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H -/* end file simdjson/generic/ondemand/value_iterator-inl.h for arm64 */ -/* end file simdjson/generic/ondemand/amalgamated.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 */ @@ -41894,13 +40427,13 @@ namespace simdjson { #undef SIMDJSON_IMPLEMENTATION /* end file simdjson/arm64/end.h */ -#endif // SIMDJSON_ARM64_ONDEMAND_H -/* end file simdjson/arm64/ondemand.h */ +#endif // SIMDJSON_ARM64_BUILDER_H +/* end file simdjson/arm64/builder.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/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 */ @@ -41916,14 +40449,14 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - /** - * Fallback implementation (runs on any machine). - */ - namespace fallback { +/** + * Fallback implementation (runs on any machine). + */ +namespace fallback { - class implementation; +class implementation; - } // namespace fallback +} // namespace fallback } // namespace simdjson #endif // SIMDJSON_FALLBACK_BASE_H @@ -41938,43 +40471,43 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace fallback { - namespace { +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; - } +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) { +/* 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; + 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); + return __builtin_clzll(input_num); #endif// _MSC_VER - } +} - } // unnamed namespace - } // namespace fallback +} // unnamed namespace +} // namespace fallback } // namespace simdjson #endif // SIMDJSON_FALLBACK_BITMANIPULATION_H @@ -41989,31 +40522,49 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace fallback { - namespace { +namespace fallback { +namespace { - // Holds backslashes and quotes locations. - struct backslash_and_quote { - public: - static constexpr uint32_t BYTES_PROCESSED = 1; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t* src, uint8_t* dst); +// 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; } + 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 + 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] }; - } +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] }; +} - } // unnamed namespace - } // namespace fallback + +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 @@ -42031,10372 +40582,1751 @@ namespace simdjson { #include #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); +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 { +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); - } +// 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(chars)); - } +/** @private */ +static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) { + return parse_eight_digits_unrolled(reinterpret_cast(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; - } +// 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; +/** @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; + // 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 + 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); + __uint128_t r = (static_cast<__uint128_t>(value1)) * value2; + answer.low = uint64_t(r); + answer.high = uint64_t(r >> 64); #endif - return answer; - } + return answer; +} - } // namespace numberparsing - } // namespace fallback +} // 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! +/* 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 -// 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 +/* 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_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; - - } // 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/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): #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 { - 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; +#if SIMDJSON_SUPPORTS_CONCEPTS - /** - * Denote that we're starting a document. - */ - simdjson_inline void start_document() noexcept; +namespace fallback { +namespace builder { + class string_builder; +}} - /** - * 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; +template +struct has_custom_serialization : std::false_type {}; - /** - * Tell whether the iterator is at the EOF mark - */ - simdjson_inline bool at_end() const noexcept; +inline constexpr struct serialize_tag { + template + constexpr void operator()(fallback::builder::string_builder& b, T&& obj) const{ + return tag_invoke(*this, b, std::forward(obj)); + } - /** - * 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; +} serialize{}; +template +struct has_custom_serialization(), std::declval())) +>> : std::true_type {}; - /** - * 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 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 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 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). - */ - simdjson_warn_unused simdjson_inline simdjson_result 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). - */ - simdjson_warn_unused simdjson_inline simdjson_result 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 has_next_field() noexcept; - - /** - * Get the current field's key. - */ - simdjson_warn_unused simdjson_inline simdjson_result field_key() 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 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 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 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 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). - */ - simdjson_warn_unused simdjson_inline simdjson_result 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). - */ - simdjson_warn_unused simdjson_inline simdjson_result 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 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 get_string(bool allow_replacement) noexcept; - template - simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_int64() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_double() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_bool() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_null() noexcept; - simdjson_warn_unused simdjson_inline bool is_negative() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_integer() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_number() noexcept; - - simdjson_warn_unused simdjson_inline simdjson_result get_root_string(bool check_trailing, bool allow_replacement) noexcept; - template - 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 get_root_wobbly_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_raw_json_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64_in_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_int64(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_int64_in_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_double(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_double_in_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_bool(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline bool is_root_negative() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_root_integer(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_number_type(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_number(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_root_null(bool check_trailing) noexcept; - - 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 reset_array() noexcept; - /** - * Restarts an object iteration. - * @returns Whether the object has any fields (returns false for empty). - */ - simdjson_inline simdjson_result 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 parse_null(const uint8_t* json) const noexcept; - simdjson_inline simdjson_result 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_inline error_code start_container(uint8_t start_char, const char* incorrect_type_message, const char* type) noexcept; - 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 advance_to_value() noexcept; - - simdjson_inline error_code incorrect_type_error(const char* message) const noexcept; - 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; - /** @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_inline error_code report_error(error_code error, const char* message) noexcept; - - friend class document; - friend class object; - friend class array; - friend class value; - friend class field; - }; // value_iterator - - } // namespace ondemand - } // namespace fallback -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public fallback::implementation_simdjson_result_base { - 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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -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, uint64_t, int64_t, double, bool - * - * You may use get_double(), get_bool(), get_uint64(), get_int64(), - * 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 simdjson_inline simdjson_result get() noexcept { - // 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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " 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."); - } - - /** - * Get this value as the given type. - * - * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool - * - * @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 simdjson_inline error_code get(T& out) noexcept; - - /** - * 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 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 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 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 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 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 get_int64_in_string() 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 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 get_double_in_string() noexcept; - - /** - * Cast this JSON value to a string. - * - * The string is guaranteed to be valid UTF-8. - * - * Equivalent to get(). - * - * Important: a value should be consumed once. Calling get_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 get_string(bool allow_replacement = false) noexcept; - - /** - * 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 - 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 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 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 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 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 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 - 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 signed 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(). - * - * @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 begin() & noexcept; - /** - * Sentinel representing the end of the array. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result 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 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 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 at(size_t index) noexcept; - /** - * Look up a field by name on an object (order-sensitive). - * - * 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 }`: - * - * ```c++ - * 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 find_field(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ - simdjson_inline simdjson_result 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. - * - * It is the default, however, 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 find_field_unordered(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result find_field_unordered(const char* key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](const char* key) noexcept; - - /** - * 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 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 is_scalar() noexcept; - /** - * Checks whether the value is a string. - * - * @returns true if the type is string - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". - */ - simdjson_inline simdjson_result is_string() noexcept; - - /** - * Checks whether the value is a negative number. - * - * @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 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 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 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 raw_json() noexcept; - - /** - * Returns the current location in the document if in bounds. - */ - simdjson_inline simdjson_result 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 at_pointer(std::string_view json_pointer) noexcept; - - /** - * Get the value associated with the given JSONPath expression. We only support - * JSONPath queries that trivially convertible to JSON Pointer queries: key - * names and array indices. - * - * @return The value associated with the given JSONPath expression, or: - * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - */ - simdjson_inline simdjson_result at_path(std::string_view at_path) noexcept; - - - protected: - /** - * Create a value. - */ - 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 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; - friend struct simdjson_result; - friend class field; - }; - - } // namespace ondemand - } // namespace fallback -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public fallback::implementation_simdjson_result_base { - 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 get_array() noexcept; - simdjson_inline simdjson_result get_object() noexcept; - - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result is_null() noexcept; - - template simdjson_inline simdjson_result get() noexcept; - - template simdjson_inline error_code get(T& out) noexcept; - -#if SIMDJSON_EXCEPTIONS - template - 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 count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - - /** - * Look up a field by name on an object (order-sensitive). - * - * 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 }`: - * - * ```c++ - * 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 find_field(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ - simdjson_inline simdjson_result 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. - * - * It is the default, however, 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 find_field_unordered(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result find_field_unordered(const char* key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](const char* key) noexcept; - - /** - * Get the type of this JSON value. - * - * 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 type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_string() noexcept; - simdjson_inline simdjson_result is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - - /** @copydoc simdjson_inline std::string_view value::raw_json_token() const noexcept */ - simdjson_inline simdjson_result raw_json_token() noexcept; - simdjson_inline simdjson_result raw_json() noexcept; - - /** @copydoc simdjson_inline simdjson_result current_location() noexcept */ - simdjson_inline simdjson_result current_location() noexcept; - /** @copydoc simdjson_inline int32_t current_depth() const noexcept */ - simdjson_inline simdjson_result current_depth() const noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - }; - -} // namespace simdjson - -#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; +template +constexpr bool require_custom_serialization = has_custom_serialization::value; #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 - 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 - 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 (`{` `}` `[` `]` `,` `:` `""` `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 value_iterator; - friend class object; - template - friend simdjson_inline void logger::log_line(const json_iterator& iter, const char* title_prefix, const char* title, std::string_view detail, int delta, int depth_delta, logger::log_level level, Args&&... args) noexcept; - template - 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 : public fallback::implementation_simdjson_result_base { - 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 }; - - 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; - - /** - * 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_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_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 unescape(raw_json_string in, bool allow_replacement) noexcept; - simdjson_inline simdjson_result unescape_wobbly(raw_json_string in) noexcept; - - simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept; - - 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 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 - 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 - 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 : public fallback::implementation_simdjson_result_base { - 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 { - // 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 - 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 - 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& type) noexcept(false); -#endif - - } // namespace ondemand - } // namespace fallback -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public fallback::implementation_simdjson_result_base { - 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; - - /** - * 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 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 unescape_wobbly(json_iterator& iter) const noexcept; - const uint8_t* buf{}; - friend class object; - friend class field; - friend class parser; - friend struct simdjson_result; - }; - - 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 : public fallback::implementation_simdjson_result_base { - 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 raw() const noexcept; - simdjson_inline simdjson_warn_unused simdjson_result unescape(fallback::ondemand::json_iterator& iter, bool allow_replacement) const noexcept; - simdjson_inline simdjson_warn_unused simdjson_result 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 - -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. - * - * @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 iterate(padded_string_view json) & noexcept; -#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON - simdjson_warn_unused simdjson_result iterate_allow_incomplete_json(padded_string_view json) & noexcept; -#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const char* json, size_t len, size_t capacity) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const uint8_t* json, size_t len, size_t capacity) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(std::string_view json, size_t capacity) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const std::string& json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(std::string& json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const simdjson_result& json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const simdjson_result& json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result 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 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. - * - * ### 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. - * @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 10MB, which has been a reasonable sweet spot in our tests. - * @param allow_comma_separated (defaults on false) This allows a mode where the documents are - * separated by commas instead of whitespace. It comes with a performance - * penalty because the entire document is indexed at once (and the document must be - * less than 4 GB), and there is no multithreading. In this mode, the batch_size parameter - * is effectively ignored, as it is set to at least the document size. - * @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 iterate_many(const uint8_t* buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ - inline simdjson_result iterate_many(const char* buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ - inline simdjson_result iterate_many(const std::string& s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - inline simdjson_result iterate_many(const std::string&& s, size_t batch_size, bool allow_comma_separated = false) = delete;// unsafe - /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ - inline simdjson_result iterate_many(const padded_string& s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - inline simdjson_result iterate_many(const padded_string&& s, size_t batch_size, bool allow_comma_separated = false) = delete;// unsafe - - /** @private We do not want to allow implicit conversion from C string to std::string. */ - simdjson_result iterate_many(const char* buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete; - - /** 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 }; -#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 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 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 - - private: - /** @private [for benchmarking access] The implementation to use */ - std::unique_ptr implementation{}; - size_t _capacity{ 0 }; - size_t _max_capacity; - size_t _max_depth{ DEFAULT_MAX_DEPTH }; - std::unique_ptr string_buf{}; -#if SIMDJSON_DEVELOPMENT_CHECKS - std::unique_ptr start_positions{}; -#endif - - friend class json_iterator; - friend class document_stream; - }; - - } // namespace ondemand - } // namespace fallback -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public fallback::implementation_simdjson_result_base { - 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): #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. - */ - simdjson_inline simdjson_result begin() noexcept; - /** - * Sentinel representing the end of the array. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result 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. - * - * To check that an array is empty, it is more performant to use - * the is_empty() method. - */ - simdjson_inline simdjson_result 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 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 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 at_pointer(std::string_view json_pointer) noexcept; - - /** - * Get the value associated with the given JSONPath expression. We only support - * JSONPath queries that trivially convertible to JSON Pointer queries: key - * names and array indices. - * - * https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00 - * - * @return The value associated with the given JSONPath expression, or: - * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - */ - inline simdjson_result at_path(std::string_view json_path) noexcept; - - /** - * Consumes the array and returns a string_view instance corresponding to the - * array as represented in JSON. It points inside the original document. - */ - simdjson_inline simdjson_result 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 at(size_t index) noexcept; - protected: - /** - * Go to the end of the array, no matter where you are right now. - */ - 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 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 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 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{}; - - friend class value; - friend class document; - friend struct simdjson_result; - friend struct simdjson_result; - friend class array_iterator; - }; - - } // namespace ondemand - } // namespace fallback -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public fallback::implementation_simdjson_result_base { - 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 begin() noexcept; - simdjson_inline simdjson_result end() noexcept; - inline simdjson_result count_elements() & noexcept; - inline simdjson_result is_empty() & noexcept; - inline simdjson_result reset() & noexcept; - simdjson_inline simdjson_result at(size_t index) noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - simdjson_inline simdjson_result raw_json() noexcept; - - }; - -} // 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 "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 exactly once per element. - * - ++ must be called exactly once in between each * (*, ++, *, ++, * ...) - */ - class array_iterator { - public: - /** Create a new, invalid array iterator. */ - simdjson_inline array_iterator() noexcept = default; - - // - // Iterator interface - // - - /** - * Get the current element. - * - * Part of the std::iterator interface. - */ - simdjson_inline simdjson_result 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; - - private: - value_iterator iter{}; - - simdjson_inline array_iterator(const value_iterator& iter) noexcept; - - friend class array; - friend class value; - friend struct simdjson_result; - }; - - } // namespace ondemand - } // namespace fallback -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public fallback::implementation_simdjson_result_base { - public: - 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 operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. - simdjson_inline bool operator==(const simdjson_result&) const noexcept; - simdjson_inline bool operator!=(const simdjson_result&) const noexcept; - simdjson_inline simdjson_result& operator++() 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): #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 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 get_object() & noexcept; - /** - * Cast this JSON value to an unsigned integer. - * - * @returns A signed 64-bit integer. - * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. - */ - simdjson_inline simdjson_result get_uint64() noexcept; - /** - * Cast this JSON value (inside string) to an unsigned integer. - * - * @returns A signed 64-bit integer. - * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. - */ - simdjson_inline simdjson_result 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 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 get_int64_in_string() 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 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 get_double_in_string() noexcept; - /** - * 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 get_string(bool allow_replacement = false) noexcept; - /** - * 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 - 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 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 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 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 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 is_null() noexcept; - - /** - * Get this value as the given type. - * - * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool - * - * You may use get_double(), get_bool(), get_uint64(), get_int64(), - * 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 simdjson_inline simdjson_result get() & noexcept { - // 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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " 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."); - } - /** @overload template simdjson_result get() & noexcept */ - template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept { - // 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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " 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."); - } - - /** - * Get this value as the given type. - * - * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 simdjson_inline error_code get(T& out) & noexcept; - /** @overload template error_code get(T &out) & noexcept */ - template simdjson_deprecated simdjson_inline error_code get(T& out) && noexcept; - -#if SIMDJSON_EXCEPTIONS - /** - * Cast this JSON value to an instance of type T. The programmer is responsible for - * providing an implementation of get 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 - explicit simdjson_inline operator T() & noexcept(false); - template - 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 signed 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. - * - * @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); - /** - * 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. - */ - simdjson_inline simdjson_result 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 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 at(size_t index) & noexcept; - /** - * Begin array iteration. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result begin() & noexcept; - /** - * Sentinel representing the end of the array. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result end() & noexcept; - - /** - * Look up a field by name on an object (order-sensitive). - * - * 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 }`: - * - * ```c++ - * 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 find_field(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result 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. - * - * It is the default, however, 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 find_field_unordered(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result find_field_unordered(const char* key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](const char* key) & noexcept; - - /** - * 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). - * - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". - */ - simdjson_inline simdjson_result 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 is_scalar() noexcept; - - /** - * Checks whether the document is a string. - * - * @returns true if the type is string - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". - */ - simdjson_inline simdjson_result is_string() noexcept; - - /** - * Checks whether the document is a negative number. - * - * @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 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 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 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 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 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 at_pointer(std::string_view json_pointer) noexcept; - - /** - * Get the value associated with the given JSONPath expression. We only support - * JSONPath queries that trivially convertible to JSON Pointer queries: key - * names and array indices. - * - * https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00 - * - * Key values are matched exactly, without unescaping or Unicode normalization. - * We do a byte-by-byte comparison. E.g. - * - * const padded_string json = "{\"\\u00E9\":123}"_padded; - * auto doc = parser.iterate(json); - * doc.at_path(".\\u00E9") == 123 - * doc.at_path((const char*)u8".\u00E9") returns an error (NO_SUCH_FIELD) - * - * @return The value associated with the given JSONPath expression, or: - * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - */ - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - - /** - * Consumes the document and returns a string_view instance corresponding to the - * document as represented in JSON. It points inside the original byte array containing - * the JSON document. - */ - simdjson_inline simdjson_result raw_json() noexcept; - protected: - /** - * Consumes the document. - */ - 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 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. - */ - 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 get_array() & noexcept; - simdjson_inline simdjson_result get_object() & noexcept; - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result get_value() noexcept; - - simdjson_inline simdjson_result is_null() noexcept; - template simdjson_inline simdjson_result get() & noexcept; - simdjson_inline simdjson_result raw_json() noexcept; - simdjson_inline operator document& () const noexcept; -#if SIMDJSON_EXCEPTIONS - template - explicit simdjson_inline operator T() noexcept(false); - simdjson_inline operator array() & noexcept(false); - simdjson_inline operator 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 raw_json_string() noexcept(false); - simdjson_inline operator bool() noexcept(false); - simdjson_inline operator value() noexcept(false); -#endif - simdjson_inline simdjson_result count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) & noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(const char* key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](const char* key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(const char* key) & noexcept; - - simdjson_inline simdjson_result type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_string() noexcept; - - simdjson_inline simdjson_result current_location() noexcept; - simdjson_inline int32_t current_depth() const noexcept; - simdjson_inline bool is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - simdjson_inline simdjson_result raw_json_token() noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - - private: - document* doc{ nullptr }; - }; - } // namespace ondemand - } // namespace fallback -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public fallback::implementation_simdjson_result_base { - 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 get_array() & noexcept; - simdjson_inline simdjson_result get_object() & noexcept; - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result get_value() noexcept; - simdjson_inline simdjson_result is_null() noexcept; - - template simdjson_inline simdjson_result get() & noexcept; - template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept; - - template simdjson_inline error_code get(T& out) & noexcept; - template simdjson_inline error_code get(T& out) && noexcept; -#if SIMDJSON_EXCEPTIONS - template ::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); - 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); - simdjson_inline operator fallback::ondemand::value() noexcept(false); -#endif - simdjson_inline simdjson_result count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) & noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(const char* key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](const char* key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(const char* key) & noexcept; - simdjson_inline simdjson_result type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_string() noexcept; - simdjson_inline simdjson_result current_location() noexcept; - simdjson_inline int32_t current_depth() const noexcept; - simdjson_inline bool at_end() const noexcept; - simdjson_inline bool is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - /** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */ - simdjson_inline simdjson_result raw_json_token() noexcept; - - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - }; - - -} // namespace simdjson - - - -namespace simdjson { - - template<> - struct simdjson_result : public fallback::implementation_simdjson_result_base { - 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 get_array() & noexcept; - simdjson_inline simdjson_result get_object() & noexcept; - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result get_value() noexcept; - simdjson_inline simdjson_result is_null() noexcept; -#if SIMDJSON_EXCEPTIONS - template ::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); - 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); - simdjson_inline operator fallback::ondemand::value() noexcept(false); -#endif - simdjson_inline simdjson_result count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) & noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(const char* key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](const char* key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(const char* key) & noexcept; - simdjson_inline simdjson_result type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_string() noexcept; - simdjson_inline simdjson_result current_location() noexcept; - simdjson_inline simdjson_result current_depth() const noexcept; - simdjson_inline simdjson_result is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - /** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */ - simdjson_inline simdjson_result raw_json_token() noexcept; - - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - }; - - -} // 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 -#include -#include -#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. - * - * ```c++ - * 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(); - * - */ - inline size_t truncated_bytes() const noexcept; - - class iterator { - public: - using value_type = simdjson_result; - using reference = simdjson_result; - using pointer = void; - 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 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. - * - * 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; - - 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; - }; - - /** - * 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) - */ - simdjson_inline document_stream( - ondemand::parser& parser, - const uint8_t* buf, - size_t len, - size_t batch_size, - bool allow_comma_separated - ) 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. */ - inline void next_document() 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; - /** - * 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 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. - */ - 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; - friend struct simdjson::internal::simdjson_result_base; - }; // document_stream - - } // namespace ondemand - } // namespace fallback -} // namespace simdjson - -namespace simdjson { - template<> - struct simdjson_result : public fallback::implementation_simdjson_result_base { - 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 so you can use C++ algorithms that rely on pairs. - */ - class field : public std::pair { - 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 unescaped_key(bool allow_replacement = false) 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. 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 - 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; - /** - * 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 start(value_iterator& parent_iter) noexcept; - static simdjson_inline simdjson_result start(const value_iterator& parent_iter, raw_json_string key) noexcept; - friend struct simdjson_result; - friend class object_iterator; - }; - - } // namespace ondemand - } // namespace fallback -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public fallback::implementation_simdjson_result_base { - 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 unescaped_key(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code unescaped_key(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result key() noexcept; - simdjson_inline simdjson_result key_raw_json_token() noexcept; - simdjson_inline simdjson_result escaped_key() noexcept; - simdjson_inline simdjson_result value() noexcept; - }; - -} // namespace simdjson - -#endif // SIMDJSON_GENERIC_ONDEMAND_FIELD_H -/* end file simdjson/generic/ondemand/field.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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -namespace simdjson { - namespace fallback { - namespace ondemand { - - /** - * 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; - - simdjson_inline simdjson_result begin() noexcept; - simdjson_inline simdjson_result end() noexcept; - /** - * Look up a field by name on an object (order-sensitive). - * - * 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 }`: - * - * ```c++ - * 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 }`. - * - * 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 find_field(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result 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. - * - * It is the default, however, 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 find_field_unordered(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) && 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("/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 at_pointer(std::string_view json_pointer) noexcept; - - /** - * Get the value associated with the given JSONPath expression. We only support - * JSONPath queries that trivially convertible to JSON Pointer queries: key - * names and array indices. - * - * @return The value associated with the given JSONPath expression, or: - * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - */ - inline simdjson_result at_path(std::string_view json_path) noexcept; - - /** - * Reset the iterator so that we are pointing back at the - * beginning of the object. You should still consume values only once even if you - * 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 reset() & 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 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 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 raw_json() noexcept; - - protected: - /** - * Go to the end of the object, no matter where you are right now. - */ - simdjson_inline error_code consume() noexcept; - static simdjson_inline simdjson_result start(value_iterator& iter) noexcept; - static simdjson_inline simdjson_result start_root(value_iterator& iter) noexcept; - static simdjson_inline simdjson_result 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{}; - - friend class value; - friend class document; - friend struct simdjson_result; - }; - - } // namespace ondemand - } // namespace fallback -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public fallback::implementation_simdjson_result_base { - 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 begin() noexcept; - simdjson_inline simdjson_result end() noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) && noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - - inline simdjson_result reset() noexcept; - inline simdjson_result is_empty() noexcept; - inline simdjson_result count_fields() & noexcept; - inline simdjson_result raw_json() noexcept; - - }; - -} // 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; - - // - // Iterator interface - // - - // Reads key and value, yielding them to the user. - // MUST ONLY BE CALLED ONCE PER ITERATION. - simdjson_inline simdjson_result 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 ',' - simdjson_inline object_iterator& operator++() noexcept; - - private: - /** - * 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; - friend class object; - }; - - } // namespace ondemand - } // namespace fallback -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public fallback::implementation_simdjson_result_base { - 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 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&) const noexcept; - // Assumes it's being compared with the end. true if depth >= iter->depth. - simdjson_inline bool operator!=(const simdjson_result&) const noexcept; - // Checks for ']' and ',' - simdjson_inline simdjson_result& 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/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 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 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 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 to_json_string(fallback::ondemand::array& x) noexcept; - inline simdjson_result to_json_string(simdjson_result x); - inline simdjson_result to_json_string(simdjson_result x); - inline simdjson_result to_json_string(simdjson_result x); - inline simdjson_result to_json_string(simdjson_result x); -} // namespace simdjson +struct has_custom_serialization : std::false_type {}; +#endif // SIMDJSON_SUPPORTS_CONCEPTS +namespace fallback { +namespace builder { /** - * 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 + * 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. */ -namespace simdjson { - namespace fallback { - namespace ondemand { +class string_builder { +public: + simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY); - /** - * 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); + 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::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 + 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 + simdjson_inline void append_key_value(key_type key, value_type value) noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + template + simdjson_inline void append_key_value(value_type value) noexcept; + + // Support for optional types (std::optional, etc.) + template + requires(!require_custom_serialization) + simdjson_inline void append(const T &opt); + + template + requires(require_custom_serialization) + simdjson_inline void append(T &&val); + + // Support for string-like types + template + requires(std::is_convertible::value || + std::is_same::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 +requires (!std::is_convertible::value && !require_custom_serialization) + 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; #if SIMDJSON_EXCEPTIONS - inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result 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 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&& 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&& 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 x); -#endif - } - } -} // namespace simdjson::fallback::ondemand - -#endif // SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H -/* end file simdjson/generic/ondemand/serialization.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/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::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::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::started(value_iterator& iter) noexcept { - bool has_value; - SIMDJSON_TRY(iter.started_array().get(has_value)); - return array(iter); - } - - simdjson_inline simdjson_result array::begin() noexcept { -#if SIMDJSON_DEVELOPMENT_CHECKS - if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } -#endif - return array_iterator(iter); - } - simdjson_inline simdjson_result array::end() noexcept { - return array_iterator(iter); - } - 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 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(starting_point), size_t(final_point - starting_point)); - } - - SIMDJSON_PUSH_DISABLE_WARNINGS - SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING - simdjson_inline simdjson_result array::count_elements() & 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 array after counting the number of elements. - iter.reset_array(); - return count; - } - SIMDJSON_POP_DISABLE_WARNINGS - - simdjson_inline simdjson_result array::is_empty() & noexcept { - bool is_not_empty; - auto error = iter.reset_array().get(is_not_empty); - if (error) { return error; } - return !is_not_empty; - } - - inline simdjson_result array::reset() & noexcept { - return iter.reset_array(); - } - - inline simdjson_result array::at_pointer(std::string_view json_pointer) noexcept { - if (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; } - - // Read the array index - size_t array_index = 0; - size_t i; - for (i = 0; i < json_pointer.length() && json_pointer[i] != '/'; i++) { - uint8_t digit = uint8_t(json_pointer[i] - '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; } - array_index = array_index * 10 + digit; - } - - // 0 followed by other digits is invalid - if (i > 1 && json_pointer[0] == '0') { return INVALID_JSON_POINTER; } // "JSON pointer array index has other characters after 0" - - // Empty string is invalid; so is a "/" with no digits before it - if (i == 0) { return INVALID_JSON_POINTER; } // "Empty string in JSON pointer array index" - // 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 std::string json_path_to_pointer_conversion(std::string_view json_path) { - if (json_path.empty() || (json_path.front() != '.' && - json_path.front() != '[')) { - return "-1"; // This is just a sentinel value, the caller should check for this and return an error. - } - - std::string result; - // Reserve space to reduce allocations, adjusting for potential increases due - // to escaping. - result.reserve(json_path.size() * 2); - - size_t i = 0; - - while (i < json_path.length()) { - if (json_path[i] == '.') { - result += '/'; - } - else if (json_path[i] == '[') { - result += '/'; - ++i; // Move past the '[' - while (i < json_path.length() && json_path[i] != ']') { - if (json_path[i] == '~') { - result += "~0"; - } - else if (json_path[i] == '/') { - result += "~1"; - } - else { - result += json_path[i]; - } - ++i; - } - if (i == json_path.length() || json_path[i] != ']') { - return "-1"; // Using sentinel value that will be handled as an error by the caller. - } - } - else { - if (json_path[i] == '~') { - result += "~0"; - } - else if (json_path[i] == '/') { - result += "~1"; - } - else { - result += json_path[i]; - } - } - ++i; - } - - return result; - } - - inline simdjson_result array::at_path(std::string_view json_path) noexcept { - auto json_pointer = json_path_to_pointer_conversion(json_path); - if (json_pointer == "-1") { return INVALID_JSON_POINTER; } - return at_pointer(json_pointer); - } - - simdjson_inline simdjson_result array::at(size_t index) noexcept { - size_t i = 0; - for (auto value : *this) { - if (i == index) { return value; } - i++; - } - return INDEX_OUT_OF_BOUNDS; - } - - } // namespace ondemand - } // namespace fallback -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result( - fallback::ondemand::array&& value - ) noexcept - : implementation_simdjson_result_base( - std::forward(value) - ) - { - } - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept - : implementation_simdjson_result_base(error) - { - } - - simdjson_inline simdjson_result simdjson_result::begin() noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() noexcept { - if (error()) { return error(); } - return first.end(); - } - simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); - } - simdjson_inline simdjson_result simdjson_result::is_empty() & noexcept { - if (error()) { return error(); } - return first.is_empty(); - } - simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } - simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { return error(); } - return first.at_path(json_path); - } - simdjson_inline simdjson_result simdjson_result::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 } - {} - - simdjson_inline simdjson_result array_iterator::operator*() noexcept { - 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 { - 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; - } - - } // namespace ondemand - } // namespace fallback -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result( - fallback::ondemand::array_iterator&& value - ) noexcept - : fallback::implementation_simdjson_result_base(std::forward(value)) - { - first.iter.assert_is_valid(); - } - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : fallback::implementation_simdjson_result_base({}, error) - { - } - - simdjson_inline simdjson_result simdjson_result::operator*() noexcept { - if (error()) { return error(); } - return *first; - } - simdjson_inline bool simdjson_result::operator==(const simdjson_result& other) const noexcept { - if (!first.iter.is_valid()) { return !error(); } - return first == other.first; - } - simdjson_inline bool simdjson_result::operator!=(const simdjson_result& other) const noexcept { - if (!first.iter.is_valid()) { return error(); } - return first != other.first; - } - simdjson_inline simdjson_result& simdjson_result::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_ARRAY_ITERATOR_INL_H -/* end file simdjson/generic/ondemand/array_iterator-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/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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -namespace simdjson { - namespace fallback { - namespace ondemand { - - simdjson_inline document::document(ondemand::json_iterator&& _iter) noexcept - : iter{ std::forward(_iter) } - { - logger::log_start_value(iter, "document"); - } - - simdjson_inline document document::start(json_iterator&& iter) noexcept { - return document(std::forward(iter)); - } - - inline void document::rewind() noexcept { - iter.rewind(); - } - - inline std::string document::to_debug_string() noexcept { - return iter.to_string(); - } - - inline simdjson_result 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 document::start_or_resume_object() noexcept { - if (iter.at_root()) { - return get_object(); - } - else { - return object::resume(resume_value_iterator()); - } - } - simdjson_inline simdjson_result document::get_value() noexcept { - // Make sure we start any arrays or objects before returning, so that start_root_() - // 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 document::get_array() & noexcept { - auto value = get_root_value_iterator(); - return array::start_root(value); - } - simdjson_inline simdjson_result 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 document::get_uint64() noexcept { - return get_root_value_iterator().get_root_uint64(true); - } - simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { - return get_root_value_iterator().get_root_uint64_in_string(true); - } - simdjson_inline simdjson_result document::get_int64() noexcept { - return get_root_value_iterator().get_root_int64(true); - } - simdjson_inline simdjson_result document::get_int64_in_string() noexcept { - return get_root_value_iterator().get_root_int64_in_string(true); - } - simdjson_inline simdjson_result document::get_double() noexcept { - return get_root_value_iterator().get_root_double(true); - } - simdjson_inline simdjson_result document::get_double_in_string() noexcept { - return get_root_value_iterator().get_root_double_in_string(true); - } - simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { - return get_root_value_iterator().get_root_string(true, allow_replacement); - } - template - 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 document::get_wobbly_string() noexcept { - return get_root_value_iterator().get_root_wobbly_string(true); - } - simdjson_inline simdjson_result document::get_raw_json_string() noexcept { - return get_root_value_iterator().get_root_raw_json_string(true); - } - simdjson_inline simdjson_result document::get_bool() noexcept { - return get_root_value_iterator().get_root_bool(true); - } - simdjson_inline simdjson_result document::is_null() noexcept { - return get_root_value_iterator().is_root_null(true); - } - - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } - - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_value(); } - - template simdjson_inline error_code document::get(T& out) & noexcept { - return get().get(out); - } - template simdjson_deprecated simdjson_inline error_code document::get(T& out) && noexcept { - return std::forward(*this).get().get(out); - } - -#if SIMDJSON_EXCEPTIONS - template - simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } - template - simdjson_inline document::operator T() & noexcept(false) { return get(); } - 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) { return get_string(false); } - simdjson_inline document::operator raw_json_string() noexcept(false) { 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 document::count_elements() & noexcept { - auto a = get_array(); - simdjson_result 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 document::count_fields() & noexcept { - auto a = get_object(); - simdjson_result 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 document::at(size_t index) & noexcept { - auto a = get_array(); - return a.at(index); - } - simdjson_inline simdjson_result document::begin() & noexcept { - return get_array().begin(); - } - simdjson_inline simdjson_result document::end() & noexcept { - return {}; - } - - simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { - return start_or_resume_object().find_field(key); - } - simdjson_inline simdjson_result document::find_field(const char* key) & noexcept { - return start_or_resume_object().find_field(key); - } - simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { - return start_or_resume_object().find_field_unordered(key); - } - simdjson_inline simdjson_result document::find_field_unordered(const char* key) & noexcept { - return start_or_resume_object().find_field_unordered(key); - } - simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { - return start_or_resume_object()[key]; - } - simdjson_inline simdjson_result document::operator[](const char* key) & noexcept { - return start_or_resume_object()[key]; - } - - simdjson_inline error_code document::consume() noexcept { - auto error = iter.skip_child(0); - if (error) { iter.abandon(); } - return error; - } - - simdjson_inline simdjson_result 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(starting_point), size_t(final_point - starting_point)); - } - - simdjson_inline simdjson_result document::type() noexcept { - return get_root_value_iterator().type(); - } - - simdjson_inline simdjson_result document::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 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 document::is_integer() noexcept { - return get_root_value_iterator().is_root_integer(true); - } - - simdjson_inline simdjson_result document::get_number_type() noexcept { - return get_root_value_iterator().get_root_number_type(true); - } - - simdjson_inline simdjson_result document::get_number() noexcept { - return get_root_value_iterator().get_root_number(true); - } - - - simdjson_inline simdjson_result document::raw_json_token() noexcept { - auto _iter = get_root_value_iterator(); - return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); - } - - simdjson_inline simdjson_result 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 document::at_path(std::string_view json_path) noexcept { - rewind(); // Rewind the document each time at_pointer is called - if (json_path.empty()) { - return this->get_value(); - } - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) { - case json_type::array: - return (*this).get_array().at_path(json_path); - case json_type::object: - return (*this).get_object().at_path(json_path); - default: - return INVALID_JSON_POINTER; - } - } - - } // namespace ondemand - } // namespace fallback -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result( - fallback::ondemand::document&& value - ) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) - { - } - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept : - implementation_simdjson_result_base( - error - ) - { - } - simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); - } - simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); - } - simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; - } - simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() & noexcept { - return {}; - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char* key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](const char* key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::find_field(const char* key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { - if (error()) { return error(); } - return first.get_array(); - } - simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { - if (error()) { return error(); } - return first.get_object(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); - } - simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); - } - simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); - } - template - simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(receiver, allow_replacement); - } - simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); - } - simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); - } - simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); - } - simdjson_inline simdjson_result simdjson_result::get_value() noexcept { - if (error()) { return error(); } - return first.get_value(); - } - simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); - } - - template - simdjson_inline simdjson_result simdjson_result::get() & noexcept { - if (error()) { return error(); } - return first.get(); - } - template - simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { - if (error()) { return error(); } - return std::forward(first).get(); - } - template - simdjson_inline error_code simdjson_result::get(T& out) & noexcept { - if (error()) { return error(); } - return first.get(out); - } - template - simdjson_inline error_code simdjson_result::get(T& out) && noexcept { - if (error()) { return error(); } - return std::forward(first).get(out); - } - - template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; - template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { - if (error()) { return error(); } - return std::forward(first); - } - template<> simdjson_inline error_code simdjson_result::get(fallback::ondemand::document& out) & noexcept = delete; - template<> simdjson_inline error_code simdjson_result::get(fallback::ondemand::document& out) && noexcept { - if (error()) { return error(); } - out = std::forward(first); - return SUCCESS; - } - - simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); - } - - simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); - } - - simdjson_inline simdjson_result simdjson_result::is_string() noexcept { - if (error()) { return error(); } - return first.is_string(); - } - - simdjson_inline bool simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); - } - - simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); - } - - simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); - } - - simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); - } - - -#if SIMDJSON_EXCEPTIONS - template ::value == false>::type> - simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator fallback::ondemand::array() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator fallback::ondemand::object() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator fallback::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator fallback::ondemand::value() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } + /** + * 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 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 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; + +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); + + /** + * 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. + */ + simdjson_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 buffer{}; + size_t position{0}; + size_t capacity{0}; + bool is_valid{true}; +}; - simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); - } - simdjson_inline bool simdjson_result::at_end() const noexcept { - if (error()) { return error(); } - return first.at_end(); - } - - - simdjson_inline int32_t simdjson_result::current_depth() const noexcept { - if (error()) { return error(); } - return first.current_depth(); - } - - simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); - } - - simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } - - simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { return error(); } - return first.at_path(json_path); - } - -} // namespace simdjson - - -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 document_reference::get_array() & noexcept { return doc->get_array(); } - simdjson_inline simdjson_result 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 document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } - simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } - simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } - simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } - simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } - simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } - simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } - template - 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 document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } - simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } - simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } - simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } - simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } -#if SIMDJSON_EXCEPTIONS - template - simdjson_inline document_reference::operator T() noexcept(false) { return get(); } - 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 document_reference::count_elements() & noexcept { return doc->count_elements(); } - simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } - simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } - simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } - simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } - simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } - simdjson_inline simdjson_result document_reference::find_field(const char* key) & noexcept { return doc->find_field(key); } - simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } - simdjson_inline simdjson_result document_reference::operator[](const char* key) & noexcept { return (*doc)[key]; } - simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } - simdjson_inline simdjson_result document_reference::find_field_unordered(const char* key) & noexcept { return doc->find_field_unordered(key); } - simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } - simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } - simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } - simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } - simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } - simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } - simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } - simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } - simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } - simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } - simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } - simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } - simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json(); } - simdjson_inline document_reference::operator document& () const noexcept { return *doc; } - - } // namespace ondemand - } // namespace fallback -} // namespace simdjson - - - -namespace simdjson { - simdjson_inline simdjson_result::simdjson_result(fallback::ondemand::document_reference value, error_code error) - noexcept : implementation_simdjson_result_base(std::forward(value), error) {} - - - simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); - } - simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); - } - simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; - } - simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() & noexcept { - return {}; - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char* key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](const char* key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::find_field(const char* key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { - if (error()) { return error(); } - return first.get_array(); - } - simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { - if (error()) { return error(); } - return first.get_object(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); - } - simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); - } - simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); - } - template - simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(receiver, allow_replacement); - } - simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); - } - simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); - } - simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); - } - simdjson_inline simdjson_result simdjson_result::get_value() noexcept { - if (error()) { return error(); } - return first.get_value(); - } - simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); - } - simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); - } - simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); - } - simdjson_inline simdjson_result simdjson_result::is_string() noexcept { - if (error()) { return error(); } - return first.is_string(); - } - simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); - } - simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); - } - simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); - } - simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); - } -#if SIMDJSON_EXCEPTIONS - template ::value == false>::type> - simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator fallback::ondemand::array() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator fallback::ondemand::object() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator fallback::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator fallback::ondemand::value() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } -#endif - - simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); - } - - simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); - } - - simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } - - simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { - return error(); - } - return first.at_path(json_path); - } - -} // namespace simdjson - -#endif // SIMDJSON_GENERIC_ONDEMAND_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 -#include - -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 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 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 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 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 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 - ) 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 }, - 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 }, - allow_comma_separated{ false }, - 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 { - 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 document_stream::iterator::operator*() noexcept { - //if(stream->error) { return stream->error; } - return simdjson_result(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::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; } - doc_index = batch_start; - 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 - 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(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; - } - } - } - - inline void document_stream::next_document() noexcept { - // 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) { doc.iter.consume_character(','); } - // 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; - if (remaining <= batch_size) { - return p.implementation->stage1(&buf[_batch_start], remaining, stage1_mode::streaming_final); - } - else { - return p.implementation->stage1(&buf[_batch_start], batch_size, stage1_mode::streaming_partial); - } - } - - 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 { - auto depth = stream->doc.iter.depth(); - auto cur_struct_index = stream->doc.iter._root - stream->parser->implementation->structural_indexes.get(); - - // If at root, process the first token to determine if scalar value - if (stream->doc.iter.at_root()) { - 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 - // TODO: We could remove trailing whitespaces - // This returns a string spanning from start of value to the beginning of the next document (excluded) - { - auto next_index = 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(stream->buf) + current_index(); - while (svlen > 1 && (std::isspace(start[svlen - 1]) || start[svlen - 1] == '\0')) { - svlen--; - } - return std::string_view(start, svlen); - } - } - cur_struct_index++; - } - - while (cur_struct_index <= static_cast(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(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::simdjson_result( - error_code error - ) noexcept : - implementation_simdjson_result_base(error) - { - } - simdjson_inline simdjson_result::simdjson_result( - fallback::ondemand::document_stream&& value - ) noexcept : - implementation_simdjson_result_base( - std::forward(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 + +#if !SIMDJSON_STATIC_REFLECTION +// fallback implementation until we have static reflection +template +simdjson_warn_unused simdjson_result 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 +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_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): #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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ +#if SIMDJSON_STATIC_REFLECTION -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() {} - - simdjson_inline field::field(raw_json_string key, ondemand::value&& value) noexcept - : std::pair(key, std::forward(value)) - { - } - - simdjson_inline simdjson_result 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::start(const value_iterator& parent_iter, raw_json_string key) noexcept { - return field(key, parent_iter.child()); - } - - simdjson_inline simdjson_warn_unused simdjson_result 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 answer = first.unescape(second.iter.json_iter(), allow_replacement); - first.consume(); - return answer; - } - - template - 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)); - 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(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(first.buf), end_quote - first.buf); - } - - simdjson_inline value& field::value() & noexcept { - return second; - } - - simdjson_inline value field::value() && noexcept { - return std::forward(*this).second; - } - - } // namespace ondemand - } // namespace fallback -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result( - fallback::ondemand::field&& value - ) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) - { - } - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept : - implementation_simdjson_result_base(error) - { - } - - simdjson_inline simdjson_result simdjson_result::key() noexcept { - if (error()) { return error(); } - return first.key(); - } - - simdjson_inline simdjson_result simdjson_result::key_raw_json_token() noexcept { - if (error()) { return error(); } - return first.key_raw_json_token(); - } - - simdjson_inline simdjson_result simdjson_result::escaped_key() noexcept { - if (error()) { return error(); } - return first.escaped_key(); - } - - simdjson_inline simdjson_result simdjson_result::unescaped_key(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.unescaped_key(allow_replacement); - } - - template - simdjson_inline error_code simdjson_result::unescaped_key(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.unescaped_key(receiver, allow_replacement); - } - - simdjson_inline simdjson_result simdjson_result::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(other.token)), - parser{ other.parser }, - _string_buf_loc{ other._string_buf_loc }, - error{ other.error }, - _depth{ other._depth }, - _root{ other._root }, - _streaming{ other._streaming } - { - 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; - 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 } - - { - 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 '"': - if (*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(TAPE_ERROR, "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; - } - - 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 { -#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(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 json_iterator::current_location() const noexcept { - if (!is_alive()) { // Unrecoverable error - if (!at_root()) { - return reinterpret_cast(token.peek(-1)); - } - else { - return reinterpret_cast(token.peek()); - } - } - if (at_end()) { - return OUT_OF_BOUNDS; - } - return reinterpret_cast(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_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 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); - SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc)); - return result; -#else - return parser->unescape(in, _string_buf_loc, allow_replacement); -#endif - } - - simdjson_inline simdjson_result json_iterator::unescape_wobbly(raw_json_string in) noexcept { -#if SIMDJSON_DEVELOPMENT_CHECKS - auto result = parser->unescape_wobbly(in, _string_buf_loc); - SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc)); - 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_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_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::simdjson_result(fallback::ondemand::json_iterator&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(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; - default: SIMDJSON_UNREACHABLE(); - } - return out; - } - -#if SIMDJSON_EXCEPTIONS - inline std::ostream& operator<<(std::ostream& out, simdjson_result& 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::simdjson_result(fallback::ondemand::json_type&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(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 +#include #include +#include +#include +#include +#include +#include +#include +// #include // for std::define_static_string - header not available yet namespace simdjson { - namespace fallback { - namespace ondemand { - namespace logger { +namespace fallback { +namespace builder { - 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. +template + requires(concepts::container_but_not_string && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + auto it = t.begin(); + auto end = t.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} - // Helper to turn unprintable or newline characters into spaces - static inline char printable_char(char c) { - if (c >= 0x20) { - return c; - } - else { - return ' '; - } - } +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +constexpr void atom(string_builder &b, const T &t) { + b.escape_and_append_with_quotes(t); +} - template - 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_s); - if (size <= 0) return std::string(); - std::unique_ptr 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 - 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)...); - } - - template - 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)...); - 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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -namespace simdjson { - namespace fallback { - namespace ondemand { - - simdjson_inline simdjson_result 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(key.size()), key.data()); - return NO_SUCH_FIELD; - } - return value(iter.child()); - } - simdjson_inline simdjson_result 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(key.size()), key.data()); - return NO_SUCH_FIELD; - } - return value(iter.child()); - } - simdjson_inline simdjson_result object::operator[](const std::string_view key) & noexcept { - return find_field_unordered(key); - } - simdjson_inline simdjson_result object::operator[](const std::string_view key) && noexcept { - return std::forward(*this).find_field_unordered(key); - } - simdjson_inline simdjson_result object::find_field(const std::string_view key) & noexcept { - 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(key.size()), key.data()); - return NO_SUCH_FIELD; - } - return value(iter.child()); - } - simdjson_inline simdjson_result object::find_field(const std::string_view key) && noexcept { - 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(key.size()), key.data()); - return NO_SUCH_FIELD; - } - return value(iter.child()); - } - - simdjson_inline simdjson_result object::start(value_iterator& iter) noexcept { - SIMDJSON_TRY(iter.start_object().error()); - return object(iter); - } - simdjson_inline simdjson_result object::start_root(value_iterator& iter) noexcept { - SIMDJSON_TRY(iter.start_root_object().error()); - return object(iter); - } - 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 object::raw_json() noexcept { - 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(starting_point), size_t(final_point - starting_point)); - } - - simdjson_inline simdjson_result 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::begin() noexcept { -#if SIMDJSON_DEVELOPMENT_CHECKS - if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } -#endif - return object_iterator(iter); - } - simdjson_inline simdjson_result object::end() noexcept { - return object_iterator(iter); - } - - inline simdjson_result object::at_pointer(std::string_view json_pointer) noexcept { - if (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 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 object::at_path(std::string_view json_path) noexcept { - auto json_pointer = json_path_to_pointer_conversion(json_path); - if (json_pointer == "-1") { - return INVALID_JSON_POINTER; - } - return at_pointer(json_pointer); - } - - simdjson_inline simdjson_result object::count_fields() & noexcept { - size_t count{ 0 }; - // Important: we do not consume any of the values. - 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 object::is_empty() & noexcept { - 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 object::reset() & noexcept { - return iter.reset_object(); - } - - } // namespace ondemand - } // namespace fallback -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result(fallback::ondemand::object&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} - - simdjson_inline simdjson_result simdjson_result::begin() noexcept { - if (error()) { return error(); } - return first.begin(); +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &m) { + if (m.empty()) { + b.append_raw("{}"); + return; + } + b.append('{'); + bool first = true; + for (const auto& [key, value] : m) { + if (!first) { + b.append(','); } - simdjson_inline simdjson_result simdjson_result::end() noexcept { - if (error()) { return error(); } - return first.end(); + first = false; + // Keys must be convertible to string_view per the concept + b.escape_and_append_with_quotes(key); + b.append(':'); + atom(b, value); + } + b.append('}'); +} + + +template::value && !std::is_same_v>::type> +constexpr void atom(string_builder &b, const number_type t) { + b.append(t); +} + +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, t.[:dm:]); + i++; + }; + b.append('}'); +} + +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &opt) { + if (opt) { + atom(b, opt.value()); + } else { + b.append_raw("null"); + } +} + +// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &ptr) { + if (ptr) { + atom(b, *ptr); + } else { + b.append_raw("null"); + } +} + +// Support for enums - serialize as string representation using expand approach from P2996R12 +template + requires(std::is_enum_v && !require_custom_serialization) +void atom(string_builder &b, const T &e) { +#if SIMDJSON_STATIC_REFLECTION + 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(std::meta::identifier_of(enum_val))); + if (e == [:enum_val:]) { + b.append_raw(enum_str); + return; } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) && noexcept { - if (error()) { return error(); } - return std::forward(first).find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) && noexcept { - if (error()) { return error(); } - return std::forward(first)[key]; - } - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) && noexcept { - if (error()) { return error(); } - return std::forward(first).find_field(key); - } - - simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } - - simdjson_inline simdjson_result simdjson_result::at_path( - std::string_view json_path) noexcept { - if (error()) { - return error(); - } - return first.at_path(json_path); - } - - inline simdjson_result simdjson_result::reset() noexcept { - if (error()) { return error(); } - return first.reset(); - } - - inline simdjson_result simdjson_result::is_empty() noexcept { - if (error()) { return error(); } - return first.is_empty(); - } - - simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); - } - - simdjson_inline simdjson_result simdjson_result::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 } - {} - - simdjson_inline simdjson_result object_iterator::operator*() noexcept { - 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 { - // 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::simdjson_result( - fallback::ondemand::object_iterator&& value - ) noexcept - : implementation_simdjson_result_base(std::forward(value)) - { - first.iter.assert_is_valid(); - } - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base({}, error) - { - } - - simdjson_inline simdjson_result simdjson_result::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::operator==(const simdjson_result& 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::operator!=(const simdjson_result& other) const noexcept { - if (!first.iter.is_valid()) { return error(); } - return first != other.first; - } - // Checks for ']' and ',' - simdjson_inline simdjson_result& simdjson_result::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/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; - 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 parser::iterate(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } - - json.remove_utf8_bom(); - - // 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(json.data()), json.length(), stage1_mode::regular)); - return document::start({ reinterpret_cast(json.data()), this }); - } - -#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON - simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } - - json.remove_utf8_bom(); - - // 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(json.data()), json.length(), stage1_mode::regular); - if (err) { - if (err != UNCLOSED_STRING) - return err; - } - return document::start({ reinterpret_cast(json.data()), this, true }); - } -#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON - - simdjson_warn_unused simdjson_inline simdjson_result 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 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 parser::iterate(std::string_view json, size_t allocated) & noexcept { - return iterate(padded_string_view(json, allocated)); - } - - simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(std::string& json) & noexcept { - if (json.capacity() - json.size() < SIMDJSON_PADDING) { - json.reserve(json.size() + SIMDJSON_PADDING); - } - return iterate(padded_string_view(json)); - } - - simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const std::string& json) & noexcept { - return iterate(padded_string_view(json)); - } - - simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const simdjson_result& result) & noexcept { - // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 parser::iterate(const simdjson_result& result) & noexcept { - // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 parser::iterate_raw(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_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(json.data()), json.length(), stage1_mode::regular)); - return json_iterator(reinterpret_cast(json.data()), this); - } - - inline simdjson_result parser::iterate_many(const uint8_t* buf, size_t len, size_t batch_size, bool allow_comma_separated) 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 (allow_comma_separated && batch_size < len) { batch_size = len; } - return document_stream(*this, buf, len, batch_size, allow_comma_separated); - } - inline simdjson_result parser::iterate_many(const char* buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept { - return iterate_many(reinterpret_cast(buf), len, batch_size, allow_comma_separated); - } - inline simdjson_result parser::iterate_many(const std::string& s, size_t batch_size, bool allow_comma_separated) noexcept { - return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated); - } - inline simdjson_result parser::iterate_many(const padded_string& s, size_t batch_size, bool allow_comma_separated) noexcept { - return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated); - } - - 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 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(dst), end - dst); - dst = end; - return result; - } - - simdjson_inline simdjson_warn_unused simdjson_result 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(dst), end - dst); - dst = end; - return result; - } - - } // namespace ondemand - } // namespace fallback -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result(fallback::ondemand::parser&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(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(buf); } - - - simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept { - size_t pos{ 0 }; - // if the content has no escape character, just scan through it quickly! - for (; pos < target.size() && target[pos] != '\\'; pos++) {} - // slow path may begin. - bool escaping{ false }; - for (; pos < target.size(); pos++) { - if ((target[pos] == '"') && !escaping) { - return false; - } - else if (target[pos] == '\\') { - escaping = !escaping; - } - else { - escaping = false; - } - } - return true; - } - - simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* target) noexcept { - size_t pos{ 0 }; - // if the content has no escape character, just scan through it quickly! - for (; target[pos] && target[pos] != '\\'; pos++) {} - // slow path may begin. - bool escaping{ false }; - for (; target[pos]; pos++) { - if ((target[pos] == '"') && !escaping) { - return false; - } - else if (target[pos] == '\\') { - escaping = !escaping; - } - else { - escaping = false; - } - } - 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()); - } - const char* r{ raw() }; - size_t pos{ 0 }; - for (; pos < target.size(); pos++) { - if (r[pos] != target[pos]) { return false; } - } - if (r[pos] != '"') { return false; } - return true; - } - - 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 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 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::simdjson_result(fallback::ondemand::raw_json_string&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} - - simdjson_inline simdjson_result simdjson_result::raw() const noexcept { - if (error()) { return error(); } - return first.raw(); - } - simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::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 simdjson_result::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/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): #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 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 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 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 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 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 to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); - } - - inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); - } - - inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); - } - - inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); - } - - inline simdjson_result to_json_string(simdjson_result 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 x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); - } + }; + // Fallback to integer if enum value not found + atom(b, static_cast>(e)); #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); - } - } + // Fallback: serialize as integer if reflection not available + atom(b, static_cast>(e)); #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 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&& x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); - } - inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& 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 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 - } +// Support for appendable containers that don't have operator[] (sets, etc.) +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &container) { + if (container.empty()) { + b.append_raw("[]"); + return; + } + b.append('['); + bool first = true; + for (const auto& item : container) { + if (!first) { + b.append(','); } -} // namespace simdjson::fallback::ondemand + first = false; + atom(b, item); + } + b.append(']'); +} -#endif // SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H -/* end file simdjson/generic/ondemand/serialization-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 +// append functions that delegate to atom functions for primitive types +template + requires(std::is_arithmetic_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +// works for struct +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^Z, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, z.[:dm:]); + i++; + }; + b.append('}'); +} + +// works for container that have begin() and end() iterators +template + requires(concepts::container_but_not_string && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + auto it = z.begin(); + auto end = z.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} + +template + requires (require_custom_serialization) +void append(string_builder &b, const Z &z) { + b.append(z); +} + + +template +simdjson_warn_unused simdjson_result to_json_string(const Z &z, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +template +simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + 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 +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 + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +void extract_from(string_builder &b, const T &obj) { + // 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) || ...); + }; + + b.append('{'); + bool first = true; + + // 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_public(mem)) { + 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 (should_extract(key)) { + if (!first) { + b.append(','); + } + first = false; + + // Serialize the key + constexpr auto quoted_key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem))); + b.append_raw(quoted_key); + b.append(':'); + + // Serialize the value + atom(b, obj.[:mem:]); + } + } + }; + + b.append('}'); +} + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + extract_from(b, obj); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +} // namespace builder +} // namespace fallback +// Alias the function template to 'to' in the global namespace +template +simdjson_warn_unused simdjson_result to_json(const Z &z, size_t initial_capacity = fallback::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + fallback::builder::string_builder b(initial_capacity); + fallback::builder::append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} +template +simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = fallback::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + fallback::builder::string_builder b(initial_capacity); + fallback::builder::append(b, z); + std::string_view view; + if(auto e = b.view().get(view); e) { return e; } + s.assign(view); + return SUCCESS; +} +// Global namespace function for extract_from +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result 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(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): #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): #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 */ -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 +#if SIMDJSON_STATIC_REFLECTION namespace simdjson { +namespace fallback { +namespace builder { - simdjson_inline simdjson_result::simdjson_result(fallback::ondemand::token_iterator&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} +/** + * 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 +simdjson_warn_unused simdjson_result 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 + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result 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(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 +simdjson_warn_unused simdjson_result 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 + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result 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(obj, opts, initial_capacity); +} } // 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-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 +#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 +#include +#include +#ifndef SIMDJSON_GENERIC_STRING_BUILDER_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): #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 SIMDJSON_EXPERIMENTAL_HAS_NEON +#include +#ifdef _MSC_VER +#include +#endif +#endif +#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 +#include +#ifdef _MSC_VER +#include +#endif +#endif + namespace simdjson { - namespace fallback { - namespace ondemand { +namespace fallback { +namespace builder { - 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; - } +static SIMDJSON_CONSTEXPR_LAMBDA std::array + 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}; - simdjson_inline simdjson_result value::get_array() noexcept { - return array::start(iter); - } - simdjson_inline simdjson_result value::get_object() noexcept { - return object::start(iter); - } - simdjson_inline simdjson_result value::start_or_resume_object() noexcept { - if (iter.at_start()) { - return get_object(); - } - else { - return object::resume(iter); - } - } +/** - simdjson_inline simdjson_result value::get_raw_json_string() noexcept { - return iter.get_raw_json_string(); - } - simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { - return iter.get_string(allow_replacement); - } - template - 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 value::get_wobbly_string() noexcept { - return iter.get_wobbly_string(); - } - simdjson_inline simdjson_result value::get_double() noexcept { - return iter.get_double(); - } - simdjson_inline simdjson_result value::get_double_in_string() noexcept { - return iter.get_double_in_string(); - } - simdjson_inline simdjson_result value::get_uint64() noexcept { - return iter.get_uint64(); - } - simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { - return iter.get_uint64_in_string(); - } - simdjson_inline simdjson_result value::get_int64() noexcept { - return iter.get_int64(); - } - simdjson_inline simdjson_result value::get_int64_in_string() noexcept { - return iter.get_int64_in_string(); - } - simdjson_inline simdjson_result value::get_bool() noexcept { - return iter.get_bool(); - } - simdjson_inline simdjson_result value::is_null() noexcept { - return iter.is_null(); - } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } +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). - template simdjson_inline error_code value::get(T& out) noexcept { - return get().get(out); - } +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; +} + +**/ + +SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline bool +simple_needs_escaping(std::string_view v) { + for (char c : v) { + // a table lookup is faster than a series of comparisons + if (json_quotable_character[static_cast(c)]) { + return true; + } + } + return false; +} + +#if SIMDJSON_EXPERIMENTAL_HAS_NEON +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + uint8x16_t running = vdupq_n_u8(0); + uint8x16_t v34 = vdupq_n_u8(34); + uint8x16_t v92 = vdupq_n_u8(92); + + for (; i + 15 < view.size(); i += 16) { + uint8x16_t word = vld1q_u8((const uint8_t *)view.data() + i); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + if (i < view.size()) { + uint8x16_t word = + vld1q_u8((const uint8_t *)view.data() + view.length() - 16); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + return vmaxvq_u32(vreinterpretq_u32_u8(running)) != 0; +} +#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2 +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + __m128i running = _mm_setzero_si128(); + for (; i + 15 < view.size(); i += 16) { + + __m128i word = + _mm_loadu_si128(reinterpret_cast(view.data() + i)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + if (i < view.size()) { + __m128i word = _mm_loadu_si128( + reinterpret_cast(view.data() + view.length() - 16)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + return _mm_movemask_epi8(running) != 0; +} +#else +simdjson_inline bool fast_needs_escaping(std::string_view view) { + return simple_needs_escaping(view); +} +#endif + +// 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(*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(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)); + + if (vmaxvq_u32(vreinterpretq_u32_u8(needs_escape)) != 0) { + // Found quotable character - extract exact byte position using ctz + uint64x2_t as64 = vreinterpretq_u64_u8(needs_escape); + uint64_t lo = vgetq_lane_u64(as64, 0); + uint64_t hi = vgetq_lane_u64(as64, 1); + size_t offset = ptr - reinterpret_cast(view.data()); +#ifdef _MSC_VER + unsigned long trailing_zero = 0; + if (lo != 0) { + _BitScanForward64(&trailing_zero, lo); + return offset + trailing_zero / 8; + } else { + _BitScanForward64(&trailing_zero, hi); + return offset + 8 + trailing_zero / 8; + } +#else + if (lo != 0) { + return offset + __builtin_ctzll(lo) / 8; + } else { + return offset + 8 + __builtin_ctzll(hi) / 8; + } +#endif + } + 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(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(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(view.data()); +#ifdef _MSC_VER + unsigned long trailing_zero = 0; + _BitScanForward(&trailing_zero, mask); + return offset + trailing_zero; +#else + return offset + __builtin_ctz(mask); +#endif + } + 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(); + } +} + +// 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 out - initout; +} + +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; +} + +simdjson_inline void string_builder::grow_buffer(size_t desired_capacity) { + if (!is_valid) { + return; + } + std::unique_ptr 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 { + +template ::value>::type> +simdjson_really_inline int int_log2(number_type x) { + return 63 - leading_zeroes(uint64_t(x) | 1); +} + +simdjson_really_inline int fast_digit_count_32(uint32_t x) { + static uint64_t table[] = { + 4294967296, 8589934582, 8589934582, 8589934582, 12884901788, + 12884901788, 12884901788, 17179868184, 17179868184, 17179868184, + 21474826480, 21474826480, 21474826480, 21474826480, 25769703776, + 25769703776, 25769703776, 30063771072, 30063771072, 30063771072, + 34349738368, 34349738368, 34349738368, 34349738368, 38554705664, + 38554705664, 38554705664, 41949672960, 41949672960, 41949672960, + 42949672960, 42949672960}; + return uint32_t((x + table[int_log2(x)]) >> 32); +} + +simdjson_really_inline int fast_digit_count_64(uint64_t x) { + static uint64_t table[] = {9, + 99, + 999, + 9999, + 99999, + 999999, + 9999999, + 99999999, + 999999999, + 9999999999, + 99999999999, + 999999999999, + 9999999999999, + 99999999999999, + 999999999999999ULL, + 9999999999999999ULL, + 99999999999999999ULL, + 999999999999999999ULL, + 9999999999999999999ULL}; + int y = (19 * int_log2(x) >> 6); + y += x > table[y]; + return y + 1; +} + +template ::value>::type> +simdjson_really_inline size_t digit_count(number_type v) noexcept { + static_assert(sizeof(number_type) == 8 || sizeof(number_type) == 4 || + sizeof(number_type) == 2 || sizeof(number_type) == 1, + "We only support 8-bit, 16-bit, 32-bit and 64-bit numbers"); + SIMDJSON_IF_CONSTEXPR(sizeof(number_type) <= 4) { + return fast_digit_count_32(static_cast(v)); + } + else { + return fast_digit_count_64(static_cast(v)); + } +} +static const char decimal_table[200] = { + 0x30, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35, + 0x30, 0x36, 0x30, 0x37, 0x30, 0x38, 0x30, 0x39, 0x31, 0x30, 0x31, 0x31, + 0x31, 0x32, 0x31, 0x33, 0x31, 0x34, 0x31, 0x35, 0x31, 0x36, 0x31, 0x37, + 0x31, 0x38, 0x31, 0x39, 0x32, 0x30, 0x32, 0x31, 0x32, 0x32, 0x32, 0x33, + 0x32, 0x34, 0x32, 0x35, 0x32, 0x36, 0x32, 0x37, 0x32, 0x38, 0x32, 0x39, + 0x33, 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x33, 0x34, 0x33, 0x35, + 0x33, 0x36, 0x33, 0x37, 0x33, 0x38, 0x33, 0x39, 0x34, 0x30, 0x34, 0x31, + 0x34, 0x32, 0x34, 0x33, 0x34, 0x34, 0x34, 0x35, 0x34, 0x36, 0x34, 0x37, + 0x34, 0x38, 0x34, 0x39, 0x35, 0x30, 0x35, 0x31, 0x35, 0x32, 0x35, 0x33, + 0x35, 0x34, 0x35, 0x35, 0x35, 0x36, 0x35, 0x37, 0x35, 0x38, 0x35, 0x39, + 0x36, 0x30, 0x36, 0x31, 0x36, 0x32, 0x36, 0x33, 0x36, 0x34, 0x36, 0x35, + 0x36, 0x36, 0x36, 0x37, 0x36, 0x38, 0x36, 0x39, 0x37, 0x30, 0x37, 0x31, + 0x37, 0x32, 0x37, 0x33, 0x37, 0x34, 0x37, 0x35, 0x37, 0x36, 0x37, 0x37, + 0x37, 0x38, 0x37, 0x39, 0x38, 0x30, 0x38, 0x31, 0x38, 0x32, 0x38, 0x33, + 0x38, 0x34, 0x38, 0x35, 0x38, 0x36, 0x38, 0x37, 0x38, 0x38, 0x38, 0x39, + 0x39, 0x30, 0x39, 0x31, 0x39, 0x32, 0x39, 0x33, 0x39, 0x34, 0x39, 0x35, + 0x39, 0x36, 0x39, 0x37, 0x39, 0x38, 0x39, 0x39, +}; +} // namespace internal + +template +simdjson_inline void string_builder::append(number_type v) noexcept { + static_assert(std::is_same::value || + std::is_integral::value || + std::is_floating_point::value, + "Unsupported number type"); + // If C++17 is available, we can 'if constexpr' here. + SIMDJSON_IF_CONSTEXPR(std::is_same::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::value) { + // Process 4 digits at a time instead of 2, reducing store operations + // and divisions by approximately half for large numbers. + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + unsigned_type pv = static_cast(v); + size_t dc = internal::digit_count(pv); + char *write_pointer = buffer.get() + position + dc - 1; + + // Process 4 digits per iteration for large numbers + while (pv >= 10000) { + unsigned_type q = pv / 10000; + unsigned_type r = pv % 10000; + unsigned_type r_hi = r / 100; // High 2 digits of remainder + unsigned_type r_lo = r % 100; // Low 2 digits of remainder + // Write low 2 digits first (rightmost), then high 2 digits + memcpy(write_pointer - 1, &internal::decimal_table[r_lo * 2], 2); + memcpy(write_pointer - 3, &internal::decimal_table[r_hi * 2], 2); + write_pointer -= 4; + pv = q; + } + + // Handle remaining 1-4 digits with original 2-digit loop + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_integral::value) { + // Same 4-digit batching as unsigned path for signed integers + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + bool negative = v < 0; + unsigned_type pv = static_cast(v); + if (negative) { + pv = 0 - pv; // the 0 is for Microsoft + } + size_t dc = internal::digit_count(pv); + // by always writing the minus sign, we avoid the branch. + buffer.get()[position] = '-'; + position += negative ? 1 : 0; + char *write_pointer = buffer.get() + position + dc - 1; + + // Process 4 digits per iteration for large numbers + while (pv >= 10000) { + unsigned_type q = pv / 10000; + unsigned_type r = pv % 10000; + unsigned_type r_hi = r / 100; + unsigned_type r_lo = r % 100; + memcpy(write_pointer - 1, &internal::decimal_table[r_lo * 2], 2); + memcpy(write_pointer - 3, &internal::decimal_table[r_hi * 2], 2); + write_pointer -= 4; + pv = q; + } + + // Handle remaining 1-4 digits + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_floating_point::value) { + constexpr size_t max_number_size = 24; + if (capacity_check(max_number_size)) { + // 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. + 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. + 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 +simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept { + escape_and_append_with_quotes(constevalutil::string_constant::value); +} +#endif + +simdjson_inline void string_builder::append_raw(const char *c) noexcept { + size_t len = std::strlen(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; + } +} +#if SIMDJSON_SUPPORTS_CONCEPTS +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +simdjson_inline void string_builder::append(const T &opt) { + if (opt) { + append(*opt); + } else { + append_null(); + } +} + +template + requires(require_custom_serialization) +simdjson_inline void string_builder::append(T &&val) { + serialize(*this, std::forward(val)); +} + +template + requires(std::is_convertible::value || + std::is_same::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 + requires(!std::is_convertible::value && !require_custom_serialization) +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>) { + 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 - template - simdjson_inline value::operator T() noexcept(false) { - return get(); - } - 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(); - } +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 value::begin() & noexcept { - return get_array().begin(); - } - simdjson_inline simdjson_result value::end() & noexcept { - return {}; - } - simdjson_inline simdjson_result value::count_elements() & noexcept { - simdjson_result 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 value::count_fields() & noexcept { - simdjson_result answer; - auto a = get_object(); - answer = a.count_fields(); - iter.move_at_start(); - return answer; - } - simdjson_inline simdjson_result value::at(size_t index) noexcept { - auto a = get_array(); - return a.at(index); - } +simdjson_inline simdjson_result +string_builder::view() const noexcept { + if (!is_valid) { + return simdjson::OUT_OF_CAPACITY; + } + return std::string_view(buffer.get(), position); +} - simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { - return start_or_resume_object().find_field(key); - } - simdjson_inline simdjson_result value::find_field(const char* key) noexcept { - return start_or_resume_object().find_field(key); - } +simdjson_inline simdjson_result string_builder::c_str() noexcept { + if (capacity_check(1)) { + buffer.get()[position] = '\0'; + return buffer.get(); + } + return simdjson::OUT_OF_CAPACITY; +} - simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { - return start_or_resume_object().find_field_unordered(key); - } - simdjson_inline simdjson_result value::find_field_unordered(const char* key) noexcept { - return start_or_resume_object().find_field_unordered(key); - } +simdjson_inline bool string_builder::validate_unicode() const noexcept { + return simdjson::validate_utf8(buffer.get(), position); +} - simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { - return start_or_resume_object()[key]; - } - simdjson_inline simdjson_result value::operator[](const char* key) noexcept { - return start_or_resume_object()[key]; - } +simdjson_inline void string_builder::start_object() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '{'; + } +} - simdjson_inline simdjson_result value::type() noexcept { - return iter.type(); - } +simdjson_inline void string_builder::end_object() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '}'; + } +} - simdjson_inline simdjson_result 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 void string_builder::start_array() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '['; + } +} - simdjson_inline simdjson_result value::is_string() noexcept { - json_type this_type; - auto error = type().get(this_type); - if (error) { return error; } - return (this_type == json_type::string); - } +simdjson_inline 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 bool value::is_negative() noexcept { - return iter.is_negative(); - } +simdjson_inline void string_builder::append_colon() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = ':'; + } +} - simdjson_inline simdjson_result value::is_integer() noexcept { - return iter.is_integer(); - } - simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { - return iter.get_number_type(); - } - simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { - return iter.get_number(); - } +template +simdjson_inline void +string_builder::append_key_value(key_type key, value_type value) noexcept { + static_assert(std::is_same::value || + std::is_convertible::value, + "Unsupported key type"); + escape_and_append_with_quotes(key); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(value); + } +} - simdjson_inline std::string_view value::raw_json_token() noexcept { - return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); - } +#if SIMDJSON_SUPPORTS_CONCEPTS +template +simdjson_inline void +string_builder::append_key_value(value_type value) noexcept { + escape_and_append_with_quotes(); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(value); + } +} +#endif - simdjson_inline simdjson_result 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 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::at_pointer(std::string_view json_pointer) noexcept { - 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::at_path(std::string_view json_path) noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) { - case json_type::array: - return (*this).get_array().at_path(json_path); - case json_type::object: - return (*this).get_object().at_path(json_path); - default: - return INVALID_JSON_POINTER; - } - } - - } // namespace ondemand - } // namespace fallback +} // namespace builder +} // namespace fallback } // namespace simdjson -namespace simdjson { +#endif // SIMDJSON_GENERIC_STRING_BUILDER_INL_H +/* end file simdjson/generic/builder/json_string_builder-inl.h for fallback */ - simdjson_inline simdjson_result::simdjson_result( - fallback::ondemand::value&& value - ) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) - { - } - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept : - implementation_simdjson_result_base(error) - { - } - simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); - } - simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); - } - simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() & noexcept { - if (error()) { return error(); } - return {}; - } - - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::find_field(const char* key) noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char* key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](const char* key) noexcept { - if (error()) { return error(); } - return first[key]; - } - - simdjson_inline simdjson_result simdjson_result::get_array() noexcept { - if (error()) { return error(); } - return first.get_array(); - } - simdjson_inline simdjson_result simdjson_result::get_object() noexcept { - if (error()) { return error(); } - return first.get_object(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); - } - simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); - } - simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); - } - template - simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(receiver, allow_replacement); - } - simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); - } - simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); - } - simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); - } - simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); - } - - template simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return first.get(); - } - template simdjson_inline error_code simdjson_result::get(T& out) noexcept { - if (error()) { return error(); } - return first.get(out); - } - - template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return std::move(first); - } - template<> simdjson_inline error_code simdjson_result::get(fallback::ondemand::value& out) noexcept { - if (error()) { return error(); } - out = first; - return SUCCESS; - } - - simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); - } - simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); - } - simdjson_inline simdjson_result simdjson_result::is_string() noexcept { - if (error()) { return error(); } - return first.is_string(); - } - simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); - } - simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); - } - simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); - } - simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); - } -#if SIMDJSON_EXCEPTIONS - template - simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return static_cast(first); - } - simdjson_inline simdjson_result::operator fallback::ondemand::array() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator fallback::ondemand::object() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator fallback::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } -#endif - - simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); - } - - simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { - if (error()) { return error(); } - return first.raw_json(); - } - - simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); - } - - simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { - if (error()) { return error(); } - return first.current_depth(); - } - - simdjson_inline simdjson_result simdjson_result::at_pointer( - std::string_view json_pointer) noexcept { - if (error()) { - return error(); - } - return first.at_pointer(json_pointer); - } - - simdjson_inline simdjson_result simdjson_result::at_path( - std::string_view json_path) noexcept { - if (error()) { - return error(); - } - return first.at_path(json_path); - } - -} // namespace simdjson - -#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H -/* end file simdjson/generic/ondemand/value-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 { - - 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 value_iterator::start_object() noexcept { - SIMDJSON_TRY(start_container('{', "Not an object", "object")); - return started_object(); - } - - simdjson_warn_unused simdjson_inline simdjson_result 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 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(); - 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 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 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 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 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 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_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 value_iterator::start_array() noexcept { - SIMDJSON_TRY(start_container('[', "Not an array", "array")); - return started_array(); - } - - simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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(!not_true); - } - simdjson_warn_unused simdjson_inline simdjson_result 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 value_iterator::get_string(bool allow_replacement) noexcept { - return get_raw_json_string().unescape(json_iter(), allow_replacement); - } - template - simdjson_warn_unused simdjson_inline error_code value_iterator::get_string(string_type& receiver, bool allow_replacement) noexcept { - std::string_view content; - auto err = get_string(allow_replacement).get(content); - if (err) { return err; } - receiver = content; - return SUCCESS; - } - simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_wobbly_string() noexcept { - return get_raw_json_string().unescape_wobbly(json_iter()); - } - simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_raw_json_string() noexcept { - auto json = peek_scalar("string"); - if (*json != '"') { return incorrect_type_error("Not a string"); } - advance_scalar("string"); - return raw_json_string(json + 1); - } - simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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 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 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 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 value_iterator::is_integer() noexcept { - return numberparsing::is_integer(peek_non_root_scalar("integer")); - } - simdjson_inline simdjson_result value_iterator::get_number_type() noexcept { - return numberparsing::get_number_type(peek_non_root_scalar("integer")); - } - simdjson_inline simdjson_result 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 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 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.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 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.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 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 - 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; - auto err = get_root_string(check_trailing, allow_replacement).get(content); - if (err) { return err; } - receiver = content; - return SUCCESS; - } - simdjson_warn_unused simdjson_inline simdjson_result 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 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; } - advance_scalar("string"); - return raw_json_string(json + 1); - } - simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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.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 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.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 value_iterator::get_root_bool(bool check_trailing) noexcept { - auto max_len = peek_root_length(); - auto json = peek_root_scalar("bool"); - uint8_t tmpbuf[5 + 1 + 1]; // +1 for null termination - tmpbuf[5 + 1] = '\0'; // make sure that buffer is always null terminated. - if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 5 + 1)) { return incorrect_type_error("Not a boolean"); } - auto result = parse_bool(tmpbuf); - if (result.error() == SUCCESS) { - if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; } - advance_root_scalar("bool"); - } - return result; - } - simdjson_inline simdjson_result 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"); - } - 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_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. - 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 simdjson_result value_iterator::reset_array() noexcept { - if (error()) { return error(); } - move_at_container_start(); - return started_array(); - } - - simdjson_inline simdjson_result 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 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 TAPE_ERROR; - } - } - - 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::simdjson_result(fallback::ondemand::value_iterator&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} - -} // namespace simdjson - -#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H -/* end file simdjson/generic/ondemand/value_iterator-inl.h for fallback */ -/* end file simdjson/generic/ondemand/amalgamated.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 */ @@ -52407,13 +42337,13 @@ namespace simdjson { #undef SIMDJSON_IMPLEMENTATION /* end file simdjson/fallback/end.h */ -#endif // SIMDJSON_FALLBACK_ONDEMAND_H -/* end file simdjson/fallback/ondemand.h */ +#endif // SIMDJSON_FALLBACK_BUILDER_H +/* end file simdjson/fallback/builder.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/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 */ @@ -52431,21 +42361,21 @@ namespace simdjson { // 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 { +/** + * Implementation for Haswell (Intel AVX2). + */ +namespace haswell { - class implementation; +class implementation; - namespace { - namespace simd { - template struct simd8; - template struct simd8x64; - } // namespace simd - } // unnamed namespace +namespace { +namespace simd { +template struct simd8; +template struct simd8x64; +} // namespace simd +} // unnamed namespace - } // namespace haswell +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_HASWELL_BASE_H @@ -52493,8 +42423,8 @@ namespace simdjson { #include #include #include // for _mm_clmulepi64_si128 - // unfortunately, we may not get _blsr_u64, but, thankfully, clang - // has it as a macro. +// 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) @@ -52507,8 +42437,14 @@ static_assert(sizeof(__m256i) <= simdjson::SIMDJSON_PADDING, "insufficient paddi /* 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 */ @@ -52522,64 +42458,64 @@ SIMDJSON_TARGET_REGION("avx2,bmi,pclmul,lzcnt,popcnt") /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace { +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) { +// 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); + 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); + //////// + // 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 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)); - } +/* 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 - } +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); - } +simdjson_inline long long int count_ones(uint64_t input_num) { + return _popcnt64(input_num); +} #endif - simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, - uint64_t* result) { +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { #if SIMDJSON_REGULAR_VISUAL_STUDIO - return _addcarry_u64(0, value1, value2, - reinterpret_cast(result)); + return _addcarry_u64(0, value1, value2, + reinterpret_cast(result)); #else - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); #endif - } +} - } // unnamed namespace - } // namespace haswell +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_HASWELL_BITMANIPULATION_H @@ -52595,24 +42531,24 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace { +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); - } +// +// 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 +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_HASWELL_BITMASK_H @@ -52632,49 +42568,49 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace numberparsing { +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(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 */ +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(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; +/** @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; + // 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 + 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); + __uint128_t r = (static_cast<__uint128_t>(value1)) * value2; + answer.low = uint64_t(r); + answer.high = uint64_t(r >> 64); #endif - return answer; - } + return answer; +} - } // namespace numberparsing - } // namespace haswell +} // namespace numberparsing +} // namespace haswell } // namespace simdjson #define SIMDJSON_SWAR_NUMBER_PARSING 1 @@ -52694,364 +42630,365 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace { - namespace simd { +namespace haswell { +namespace { +namespace simd { - // Forward-declared so they can be used by splat and friends. - template - struct base { - __m256i value; + // Forward-declared so they can be used by splat and friends. + template + struct base { + __m256i value; - // Zero constructor - simdjson_inline base() : value{ __m256i() } {} + // Zero constructor + simdjson_inline base() : value{__m256i()} {} - // Conversion from SIMD register - simdjson_inline base(const __m256i _value) : value(_value) {} + // 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; } + // 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(this); *this_cast = *this_cast | other; return *this_cast; } - simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } - simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } - }; + // 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(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; - // Forward-declared so they can be used by splat and friends. - template - struct simd8; + // Forward-declared so they can be used by splat and friends. + template + struct simd8; - template> - struct base8 : base> { - typedef uint32_t bitmask_t; - typedef uint64_t bitmask2_t; + template> + struct base8: base> { + typedef uint32_t bitmask_t; + typedef uint64_t bitmask2_t; - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m256i _value) : base>(_value) {} + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m256i _value) : base>(_value) {} - friend simdjson_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm256_cmpeq_epi8(lhs, rhs); } + friend simdjson_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm256_cmpeq_epi8(lhs, rhs); } - static const int SIZE = sizeof(base::value); + static const int SIZE = sizeof(base::value); - template - simdjson_inline simd8 prev(const simd8 prev_chunk) const { - return _mm256_alignr_epi8(*this, _mm256_permute2x128_si256(prev_chunk, *this, 0x21), 16 - N); - } - }; + template + simdjson_inline simd8 prev(const simd8 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 : base8 { - static simdjson_inline simd8 splat(bool _value) { return _mm256_set1_epi8(uint8_t(-(!!_value))); } + // SIMD byte mask type (returned by things like eq and gt) + template<> + struct simd8: base8 { + static simdjson_inline simd8 splat(bool _value) { return _mm256_set1_epi8(uint8_t(-(!!_value))); } - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m256i _value) : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) : base8(splat(_value)) {} + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m256i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(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 operator~() const { return *this ^ true; } - }; + 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 operator~() const { return *this ^ true; } + }; - template - struct base8_numeric : base8 { - static simdjson_inline simd8 splat(T _value) { return _mm256_set1_epi8(_value); } - static simdjson_inline simd8 zero() { return _mm256_setzero_si256(); } - static simdjson_inline simd8 load(const T values[32]) { - return _mm256_loadu_si256(reinterpret_cast(values)); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdjson_inline simd8 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( - 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 - ); - } + template + struct base8_numeric: base8 { + static simdjson_inline simd8 splat(T _value) { return _mm256_set1_epi8(_value); } + static simdjson_inline simd8 zero() { return _mm256_setzero_si256(); } + static simdjson_inline simd8 load(const T values[32]) { + return _mm256_loadu_si256(reinterpret_cast(values)); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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( + 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() {} - simdjson_inline base8_numeric(const __m256i _value) : base8(_value) {} + simdjson_inline base8_numeric() : base8() {} + simdjson_inline base8_numeric(const __m256i _value) : base8(_value) {} - // Store to array - simdjson_inline void store(T dst[32]) const { return _mm256_storeu_si256(reinterpret_cast<__m256i*>(dst), *this); } + // 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 operator+(const simd8 other) const { return _mm256_add_epi8(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return _mm256_sub_epi8(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { return _mm256_add_epi8(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return _mm256_sub_epi8(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } - // Override to distinguish from bool version - simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return _mm256_shuffle_epi8(lookup_table, *this); - } + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 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 compress(uint32_t mask) would be - // sensible, but the AVX ISA makes this kind of approach difficult. - template - 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]; + // 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 compress(uint32_t mask) would be + // sensible, but the AVX ISA makes this kind of approach difficult. + template + 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(pshufb_combine_table + pop1 * 8))); - __m256i compactmask = _mm256_insertf128_si256(v256, - _mm_loadu_si128(reinterpret_cast(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); - } + // 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(pshufb_combine_table + pop1 * 8))); + __m256i compactmask = _mm256_insertf128_si256(v256, + _mm_loadu_si128(reinterpret_cast(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 - simdjson_inline simd8 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::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - }; + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; - // Signed bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( - 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 - ); - } + // Signed bytes + template<> + struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( + 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 max_val(const simd8 other) const { return _mm256_max_epi8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm256_min_epi8(*this, other); } - simdjson_inline simd8 operator>(const simd8 other) const { return _mm256_cmpgt_epi8(*this, other); } - simdjson_inline simd8 operator<(const simd8 other) const { return _mm256_cmpgt_epi8(other, *this); } - }; + // Order-sensitive comparisons + simdjson_inline simd8 max_val(const simd8 other) const { return _mm256_max_epi8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm256_min_epi8(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return _mm256_cmpgt_epi8(*this, other); } + simdjson_inline simd8 operator<(const simd8 other) const { return _mm256_cmpgt_epi8(other, *this); } + }; - // Unsigned bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( - 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 - ); - } + // Unsigned bytes + template<> + struct simd8: base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( + 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 saturating_add(const simd8 other) const { return _mm256_adds_epu8(*this, other); } - simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm256_subs_epu8(*this, other); } + // Saturated math + simdjson_inline simd8 saturating_add(const simd8 other) const { return _mm256_adds_epu8(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm256_subs_epu8(*this, other); } - // Order-specific operations - simdjson_inline simd8 max_val(const simd8 other) const { return _mm256_max_epu8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm256_min_epu8(other, *this); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } - simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } - simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } - simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } + // Order-specific operations + simdjson_inline simd8 max_val(const simd8 other) const { return _mm256_max_epu8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm256_min_epu8(other, *this); } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } + simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } + simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } + simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } - // Bit-specific operations - simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } - simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } - simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } - simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return _mm256_testz_si256(*this, bits); } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } - template - simdjson_inline simd8 shr() const { return simd8(_mm256_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } - template - simdjson_inline simd8 shl() const { return simd8(_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 - simdjson_inline int get_bit() const { return _mm256_movemask_epi8(_mm256_slli_epi16(*this, 7 - N)); } - }; + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } + simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } + simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } + simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return _mm256_testz_si256(*this, bits); } + simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } + template + simdjson_inline simd8 shr() const { return simd8(_mm256_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } + template + simdjson_inline simd8 shl() const { return simd8(_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 + simdjson_inline int get_bit() const { return _mm256_movemask_epi8(_mm256_slli_epi16(*this, 7-N)); } + }; - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 2, "Haswell kernel should use two registers per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 2, "Haswell kernel should use two registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1) : chunks{ chunk0, chunk1 } {} - simdjson_inline simd8x64(const T ptr[64]) : chunks{ simd8::load(ptr), simd8::load(ptr + 32) } {} + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1) : chunks{chunk0, chunk1} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::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 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) * 0); - this->chunks[1].store(ptr + sizeof(simd8) * 1); - } + simdjson_inline void store(T ptr[64]) const { + this->chunks[0].store(ptr+sizeof(simd8)*0); + this->chunks[1].store(ptr+sizeof(simd8)*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 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 reduce_or() const { - return this->chunks[0] | this->chunks[1]; - } + simdjson_inline simd8 reduce_or() const { + return this->chunks[0] | this->chunks[1]; + } - simdjson_inline simd8x64 bit_or(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] | mask, - this->chunks[1] | mask - ); - } + simdjson_inline simd8x64 bit_or(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] | mask, + this->chunks[1] | mask + ); + } - simdjson_inline uint64_t eq(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] == mask, - this->chunks[1] == mask - ).to_bitmask(); - } + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] == mask, + this->chunks[1] == mask + ).to_bitmask(); + } - simdjson_inline uint64_t eq(const simd8x64& other) const { - return simd8x64( - this->chunks[0] == other.chunks[0], - this->chunks[1] == other.chunks[1] - ).to_bitmask(); - } + simdjson_inline uint64_t eq(const simd8x64 &other) const { + return simd8x64( + 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] <= mask, - this->chunks[1] <= mask - ).to_bitmask(); - } - }; // struct simd8x64 + simdjson_inline uint64_t lteq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] <= mask, + this->chunks[1] <= mask + ).to_bitmask(); + } + }; // struct simd8x64 - } // namespace simd +} // namespace simd - } // unnamed namespace - } // namespace haswell +} // unnamed namespace +} // namespace haswell } // namespace simdjson #endif // SIMDJSON_HASWELL_SIMD_H @@ -53068,10341 +43005,1739 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace haswell { - namespace { +namespace haswell { +namespace { - using namespace simd; +using namespace simd; - // Holds backslashes and quotes locations. - struct backslash_and_quote { - public: - static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t* src, uint8_t* dst); +// 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); } + 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 + 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 v(src); - // store to dest unconditionally - we can overwrite the bits we don't like later - v.store(dst); - return { - static_cast((v == '\\').to_bitmask()), // bs_bits - static_cast((v == '"').to_bitmask()), // quote_bits - }; - } +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 v(src); + // store to dest unconditionally - we can overwrite the bits we don't like later + v.store(dst); + return { + static_cast((v == '\\').to_bitmask()), // bs_bits + static_cast((v == '"').to_bitmask()), // quote_bits + }; +} - } // unnamed namespace - } // namespace haswell + +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 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 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! +/* 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 -// 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 +/* 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_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; - - } // 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/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): #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 { - 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; +#if SIMDJSON_SUPPORTS_CONCEPTS - /** - * Denote that we're starting a document. - */ - simdjson_inline void start_document() noexcept; +namespace haswell { +namespace builder { + class string_builder; +}} - /** - * 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; +template +struct has_custom_serialization : std::false_type {}; - /** - * Tell whether the iterator is at the EOF mark - */ - simdjson_inline bool at_end() const noexcept; +inline constexpr struct serialize_tag { + template + constexpr void operator()(haswell::builder::string_builder& b, T&& obj) const{ + return tag_invoke(*this, b, std::forward(obj)); + } - /** - * 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; +} serialize{}; +template +struct has_custom_serialization(), std::declval())) +>> : std::true_type {}; - /** - * 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 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 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 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). - */ - simdjson_warn_unused simdjson_inline simdjson_result 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). - */ - simdjson_warn_unused simdjson_inline simdjson_result 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 has_next_field() noexcept; - - /** - * Get the current field's key. - */ - simdjson_warn_unused simdjson_inline simdjson_result field_key() 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 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 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 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 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). - */ - simdjson_warn_unused simdjson_inline simdjson_result 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). - */ - simdjson_warn_unused simdjson_inline simdjson_result 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 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 get_string(bool allow_replacement) noexcept; - template - simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_int64() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_double() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_bool() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_null() noexcept; - simdjson_warn_unused simdjson_inline bool is_negative() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_integer() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_number() noexcept; - - simdjson_warn_unused simdjson_inline simdjson_result get_root_string(bool check_trailing, bool allow_replacement) noexcept; - template - 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 get_root_wobbly_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_raw_json_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64_in_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_int64(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_int64_in_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_double(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_double_in_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_bool(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline bool is_root_negative() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_root_integer(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_number_type(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_number(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_root_null(bool check_trailing) noexcept; - - 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 reset_array() noexcept; - /** - * Restarts an object iteration. - * @returns Whether the object has any fields (returns false for empty). - */ - simdjson_inline simdjson_result 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 parse_null(const uint8_t* json) const noexcept; - simdjson_inline simdjson_result 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_inline error_code start_container(uint8_t start_char, const char* incorrect_type_message, const char* type) noexcept; - 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 advance_to_value() noexcept; - - simdjson_inline error_code incorrect_type_error(const char* message) const noexcept; - 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; - /** @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_inline error_code report_error(error_code error, const char* message) noexcept; - - friend class document; - friend class object; - friend class array; - friend class value; - friend class field; - }; // value_iterator - - } // namespace ondemand - } // namespace haswell -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public haswell::implementation_simdjson_result_base { - 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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -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, uint64_t, int64_t, double, bool - * - * You may use get_double(), get_bool(), get_uint64(), get_int64(), - * 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 simdjson_inline simdjson_result get() noexcept { - // 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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " 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."); - } - - /** - * Get this value as the given type. - * - * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool - * - * @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 simdjson_inline error_code get(T& out) noexcept; - - /** - * 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 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 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 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 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 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 get_int64_in_string() 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 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 get_double_in_string() noexcept; - - /** - * Cast this JSON value to a string. - * - * The string is guaranteed to be valid UTF-8. - * - * Equivalent to get(). - * - * Important: a value should be consumed once. Calling get_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 get_string(bool allow_replacement = false) noexcept; - - /** - * 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 - 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 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 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 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 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 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 - 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 signed 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(). - * - * @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 begin() & noexcept; - /** - * Sentinel representing the end of the array. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result 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 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 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 at(size_t index) noexcept; - /** - * Look up a field by name on an object (order-sensitive). - * - * 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 }`: - * - * ```c++ - * 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 find_field(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ - simdjson_inline simdjson_result 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. - * - * It is the default, however, 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 find_field_unordered(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result find_field_unordered(const char* key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](const char* key) noexcept; - - /** - * 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 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 is_scalar() noexcept; - /** - * Checks whether the value is a string. - * - * @returns true if the type is string - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". - */ - simdjson_inline simdjson_result is_string() noexcept; - - /** - * Checks whether the value is a negative number. - * - * @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 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 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 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 raw_json() noexcept; - - /** - * Returns the current location in the document if in bounds. - */ - simdjson_inline simdjson_result 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 at_pointer(std::string_view json_pointer) noexcept; - - /** - * Get the value associated with the given JSONPath expression. We only support - * JSONPath queries that trivially convertible to JSON Pointer queries: key - * names and array indices. - * - * @return The value associated with the given JSONPath expression, or: - * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - */ - simdjson_inline simdjson_result at_path(std::string_view at_path) noexcept; - - - protected: - /** - * Create a value. - */ - 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 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; - friend struct simdjson_result; - friend class field; - }; - - } // namespace ondemand - } // namespace haswell -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public haswell::implementation_simdjson_result_base { - 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 get_array() noexcept; - simdjson_inline simdjson_result get_object() noexcept; - - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result is_null() noexcept; - - template simdjson_inline simdjson_result get() noexcept; - - template simdjson_inline error_code get(T& out) noexcept; - -#if SIMDJSON_EXCEPTIONS - template - 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 count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - - /** - * Look up a field by name on an object (order-sensitive). - * - * 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 }`: - * - * ```c++ - * 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 find_field(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ - simdjson_inline simdjson_result 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. - * - * It is the default, however, 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 find_field_unordered(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result find_field_unordered(const char* key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](const char* key) noexcept; - - /** - * Get the type of this JSON value. - * - * 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 type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_string() noexcept; - simdjson_inline simdjson_result is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - - /** @copydoc simdjson_inline std::string_view value::raw_json_token() const noexcept */ - simdjson_inline simdjson_result raw_json_token() noexcept; - simdjson_inline simdjson_result raw_json() noexcept; - - /** @copydoc simdjson_inline simdjson_result current_location() noexcept */ - simdjson_inline simdjson_result current_location() noexcept; - /** @copydoc simdjson_inline int32_t current_depth() const noexcept */ - simdjson_inline simdjson_result current_depth() const noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - }; - -} // namespace simdjson - -#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; +template +constexpr bool require_custom_serialization = has_custom_serialization::value; #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 - 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 - 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 (`{` `}` `[` `]` `,` `:` `""` `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 value_iterator; - friend class object; - template - friend simdjson_inline void logger::log_line(const json_iterator& iter, const char* title_prefix, const char* title, std::string_view detail, int delta, int depth_delta, logger::log_level level, Args&&... args) noexcept; - template - 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 : public haswell::implementation_simdjson_result_base { - 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 }; - - 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; - - /** - * 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_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_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 unescape(raw_json_string in, bool allow_replacement) noexcept; - simdjson_inline simdjson_result unescape_wobbly(raw_json_string in) noexcept; - - simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept; - - 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 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 - 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 - 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 : public haswell::implementation_simdjson_result_base { - 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 { - // 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 - 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 - 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& type) noexcept(false); -#endif - - } // namespace ondemand - } // namespace haswell -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public haswell::implementation_simdjson_result_base { - 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; - - /** - * 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 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 unescape_wobbly(json_iterator& iter) const noexcept; - const uint8_t* buf{}; - friend class object; - friend class field; - friend class parser; - friend struct simdjson_result; - }; - - 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 : public haswell::implementation_simdjson_result_base { - 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 raw() const noexcept; - simdjson_inline simdjson_warn_unused simdjson_result unescape(haswell::ondemand::json_iterator& iter, bool allow_replacement) const noexcept; - simdjson_inline simdjson_warn_unused simdjson_result 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 - -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. - * - * @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 iterate(padded_string_view json) & noexcept; -#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON - simdjson_warn_unused simdjson_result iterate_allow_incomplete_json(padded_string_view json) & noexcept; -#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const char* json, size_t len, size_t capacity) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const uint8_t* json, size_t len, size_t capacity) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(std::string_view json, size_t capacity) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const std::string& json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(std::string& json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const simdjson_result& json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const simdjson_result& json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result 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 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. - * - * ### 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. - * @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 10MB, which has been a reasonable sweet spot in our tests. - * @param allow_comma_separated (defaults on false) This allows a mode where the documents are - * separated by commas instead of whitespace. It comes with a performance - * penalty because the entire document is indexed at once (and the document must be - * less than 4 GB), and there is no multithreading. In this mode, the batch_size parameter - * is effectively ignored, as it is set to at least the document size. - * @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 iterate_many(const uint8_t* buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ - inline simdjson_result iterate_many(const char* buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ - inline simdjson_result iterate_many(const std::string& s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - inline simdjson_result iterate_many(const std::string&& s, size_t batch_size, bool allow_comma_separated = false) = delete;// unsafe - /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ - inline simdjson_result iterate_many(const padded_string& s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - inline simdjson_result iterate_many(const padded_string&& s, size_t batch_size, bool allow_comma_separated = false) = delete;// unsafe - - /** @private We do not want to allow implicit conversion from C string to std::string. */ - simdjson_result iterate_many(const char* buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete; - - /** 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 }; -#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 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 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 - - private: - /** @private [for benchmarking access] The implementation to use */ - std::unique_ptr implementation{}; - size_t _capacity{ 0 }; - size_t _max_capacity; - size_t _max_depth{ DEFAULT_MAX_DEPTH }; - std::unique_ptr string_buf{}; -#if SIMDJSON_DEVELOPMENT_CHECKS - std::unique_ptr start_positions{}; -#endif - - friend class json_iterator; - friend class document_stream; - }; - - } // namespace ondemand - } // namespace haswell -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public haswell::implementation_simdjson_result_base { - 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): #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. - */ - simdjson_inline simdjson_result begin() noexcept; - /** - * Sentinel representing the end of the array. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result 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. - * - * To check that an array is empty, it is more performant to use - * the is_empty() method. - */ - simdjson_inline simdjson_result 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 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 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 at_pointer(std::string_view json_pointer) noexcept; - - /** - * Get the value associated with the given JSONPath expression. We only support - * JSONPath queries that trivially convertible to JSON Pointer queries: key - * names and array indices. - * - * https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00 - * - * @return The value associated with the given JSONPath expression, or: - * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - */ - inline simdjson_result at_path(std::string_view json_path) noexcept; - - /** - * Consumes the array and returns a string_view instance corresponding to the - * array as represented in JSON. It points inside the original document. - */ - simdjson_inline simdjson_result 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 at(size_t index) noexcept; - protected: - /** - * Go to the end of the array, no matter where you are right now. - */ - 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 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 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 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{}; - - friend class value; - friend class document; - friend struct simdjson_result; - friend struct simdjson_result; - friend class array_iterator; - }; - - } // namespace ondemand - } // namespace haswell -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public haswell::implementation_simdjson_result_base { - 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 begin() noexcept; - simdjson_inline simdjson_result end() noexcept; - inline simdjson_result count_elements() & noexcept; - inline simdjson_result is_empty() & noexcept; - inline simdjson_result reset() & noexcept; - simdjson_inline simdjson_result at(size_t index) noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - simdjson_inline simdjson_result raw_json() noexcept; - - }; - -} // 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 "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 exactly once per element. - * - ++ must be called exactly once in between each * (*, ++, *, ++, * ...) - */ - class array_iterator { - public: - /** Create a new, invalid array iterator. */ - simdjson_inline array_iterator() noexcept = default; - - // - // Iterator interface - // - - /** - * Get the current element. - * - * Part of the std::iterator interface. - */ - simdjson_inline simdjson_result 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; - - private: - value_iterator iter{}; - - simdjson_inline array_iterator(const value_iterator& iter) noexcept; - - friend class array; - friend class value; - friend struct simdjson_result; - }; - - } // namespace ondemand - } // namespace haswell -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public haswell::implementation_simdjson_result_base { - public: - 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 operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. - simdjson_inline bool operator==(const simdjson_result&) const noexcept; - simdjson_inline bool operator!=(const simdjson_result&) const noexcept; - simdjson_inline simdjson_result& operator++() 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): #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 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 get_object() & noexcept; - /** - * Cast this JSON value to an unsigned integer. - * - * @returns A signed 64-bit integer. - * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. - */ - simdjson_inline simdjson_result get_uint64() noexcept; - /** - * Cast this JSON value (inside string) to an unsigned integer. - * - * @returns A signed 64-bit integer. - * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. - */ - simdjson_inline simdjson_result 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 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 get_int64_in_string() 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 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 get_double_in_string() noexcept; - /** - * 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 get_string(bool allow_replacement = false) noexcept; - /** - * 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 - 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 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 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 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 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 is_null() noexcept; - - /** - * Get this value as the given type. - * - * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool - * - * You may use get_double(), get_bool(), get_uint64(), get_int64(), - * 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 simdjson_inline simdjson_result get() & noexcept { - // 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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " 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."); - } - /** @overload template simdjson_result get() & noexcept */ - template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept { - // 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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " 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."); - } - - /** - * Get this value as the given type. - * - * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 simdjson_inline error_code get(T& out) & noexcept; - /** @overload template error_code get(T &out) & noexcept */ - template simdjson_deprecated simdjson_inline error_code get(T& out) && noexcept; - -#if SIMDJSON_EXCEPTIONS - /** - * Cast this JSON value to an instance of type T. The programmer is responsible for - * providing an implementation of get 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 - explicit simdjson_inline operator T() & noexcept(false); - template - 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 signed 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. - * - * @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); - /** - * 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. - */ - simdjson_inline simdjson_result 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 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 at(size_t index) & noexcept; - /** - * Begin array iteration. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result begin() & noexcept; - /** - * Sentinel representing the end of the array. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result end() & noexcept; - - /** - * Look up a field by name on an object (order-sensitive). - * - * 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 }`: - * - * ```c++ - * 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 find_field(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result 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. - * - * It is the default, however, 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 find_field_unordered(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result find_field_unordered(const char* key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](const char* key) & noexcept; - - /** - * 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). - * - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". - */ - simdjson_inline simdjson_result 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 is_scalar() noexcept; - - /** - * Checks whether the document is a string. - * - * @returns true if the type is string - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". - */ - simdjson_inline simdjson_result is_string() noexcept; - - /** - * Checks whether the document is a negative number. - * - * @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 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 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 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 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 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 at_pointer(std::string_view json_pointer) noexcept; - - /** - * Get the value associated with the given JSONPath expression. We only support - * JSONPath queries that trivially convertible to JSON Pointer queries: key - * names and array indices. - * - * https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00 - * - * Key values are matched exactly, without unescaping or Unicode normalization. - * We do a byte-by-byte comparison. E.g. - * - * const padded_string json = "{\"\\u00E9\":123}"_padded; - * auto doc = parser.iterate(json); - * doc.at_path(".\\u00E9") == 123 - * doc.at_path((const char*)u8".\u00E9") returns an error (NO_SUCH_FIELD) - * - * @return The value associated with the given JSONPath expression, or: - * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - */ - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - - /** - * Consumes the document and returns a string_view instance corresponding to the - * document as represented in JSON. It points inside the original byte array containing - * the JSON document. - */ - simdjson_inline simdjson_result raw_json() noexcept; - protected: - /** - * Consumes the document. - */ - 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 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. - */ - 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 get_array() & noexcept; - simdjson_inline simdjson_result get_object() & noexcept; - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result get_value() noexcept; - - simdjson_inline simdjson_result is_null() noexcept; - template simdjson_inline simdjson_result get() & noexcept; - simdjson_inline simdjson_result raw_json() noexcept; - simdjson_inline operator document& () const noexcept; -#if SIMDJSON_EXCEPTIONS - template - explicit simdjson_inline operator T() noexcept(false); - simdjson_inline operator array() & noexcept(false); - simdjson_inline operator 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 raw_json_string() noexcept(false); - simdjson_inline operator bool() noexcept(false); - simdjson_inline operator value() noexcept(false); -#endif - simdjson_inline simdjson_result count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) & noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(const char* key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](const char* key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(const char* key) & noexcept; - - simdjson_inline simdjson_result type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_string() noexcept; - - simdjson_inline simdjson_result current_location() noexcept; - simdjson_inline int32_t current_depth() const noexcept; - simdjson_inline bool is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - simdjson_inline simdjson_result raw_json_token() noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - - private: - document* doc{ nullptr }; - }; - } // namespace ondemand - } // namespace haswell -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public haswell::implementation_simdjson_result_base { - 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 get_array() & noexcept; - simdjson_inline simdjson_result get_object() & noexcept; - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result get_value() noexcept; - simdjson_inline simdjson_result is_null() noexcept; - - template simdjson_inline simdjson_result get() & noexcept; - template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept; - - template simdjson_inline error_code get(T& out) & noexcept; - template simdjson_inline error_code get(T& out) && noexcept; -#if SIMDJSON_EXCEPTIONS - template ::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); - 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); - simdjson_inline operator haswell::ondemand::value() noexcept(false); -#endif - simdjson_inline simdjson_result count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) & noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(const char* key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](const char* key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(const char* key) & noexcept; - simdjson_inline simdjson_result type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_string() noexcept; - simdjson_inline simdjson_result current_location() noexcept; - simdjson_inline int32_t current_depth() const noexcept; - simdjson_inline bool at_end() const noexcept; - simdjson_inline bool is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - /** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */ - simdjson_inline simdjson_result raw_json_token() noexcept; - - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - }; - - -} // namespace simdjson - - - -namespace simdjson { - - template<> - struct simdjson_result : public haswell::implementation_simdjson_result_base { - 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 get_array() & noexcept; - simdjson_inline simdjson_result get_object() & noexcept; - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result get_value() noexcept; - simdjson_inline simdjson_result is_null() noexcept; -#if SIMDJSON_EXCEPTIONS - template ::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); - 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); - simdjson_inline operator haswell::ondemand::value() noexcept(false); -#endif - simdjson_inline simdjson_result count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) & noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(const char* key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](const char* key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(const char* key) & noexcept; - simdjson_inline simdjson_result type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_string() noexcept; - simdjson_inline simdjson_result current_location() noexcept; - simdjson_inline simdjson_result current_depth() const noexcept; - simdjson_inline simdjson_result is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - /** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */ - simdjson_inline simdjson_result raw_json_token() noexcept; - - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - }; - - -} // 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 -#include -#include -#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. - * - * ```c++ - * 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(); - * - */ - inline size_t truncated_bytes() const noexcept; - - class iterator { - public: - using value_type = simdjson_result; - using reference = simdjson_result; - using pointer = void; - 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 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. - * - * 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; - - 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; - }; - - /** - * 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) - */ - simdjson_inline document_stream( - ondemand::parser& parser, - const uint8_t* buf, - size_t len, - size_t batch_size, - bool allow_comma_separated - ) 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. */ - inline void next_document() 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; - /** - * 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 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. - */ - 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; - friend struct simdjson::internal::simdjson_result_base; - }; // document_stream - - } // namespace ondemand - } // namespace haswell -} // namespace simdjson - -namespace simdjson { - template<> - struct simdjson_result : public haswell::implementation_simdjson_result_base { - 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 so you can use C++ algorithms that rely on pairs. - */ - class field : public std::pair { - 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 unescaped_key(bool allow_replacement = false) 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. 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 - 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; - /** - * 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 start(value_iterator& parent_iter) noexcept; - static simdjson_inline simdjson_result start(const value_iterator& parent_iter, raw_json_string key) noexcept; - friend struct simdjson_result; - friend class object_iterator; - }; - - } // namespace ondemand - } // namespace haswell -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public haswell::implementation_simdjson_result_base { - 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 unescaped_key(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code unescaped_key(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result key() noexcept; - simdjson_inline simdjson_result key_raw_json_token() noexcept; - simdjson_inline simdjson_result escaped_key() noexcept; - simdjson_inline simdjson_result value() noexcept; - }; - -} // namespace simdjson - -#endif // SIMDJSON_GENERIC_ONDEMAND_FIELD_H -/* end file simdjson/generic/ondemand/field.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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -namespace simdjson { - namespace haswell { - namespace ondemand { - - /** - * 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; - - simdjson_inline simdjson_result begin() noexcept; - simdjson_inline simdjson_result end() noexcept; - /** - * Look up a field by name on an object (order-sensitive). - * - * 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 }`: - * - * ```c++ - * 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 }`. - * - * 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 find_field(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result 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. - * - * It is the default, however, 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 find_field_unordered(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) && 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("/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 at_pointer(std::string_view json_pointer) noexcept; - - /** - * Get the value associated with the given JSONPath expression. We only support - * JSONPath queries that trivially convertible to JSON Pointer queries: key - * names and array indices. - * - * @return The value associated with the given JSONPath expression, or: - * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - */ - inline simdjson_result at_path(std::string_view json_path) noexcept; - - /** - * Reset the iterator so that we are pointing back at the - * beginning of the object. You should still consume values only once even if you - * 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 reset() & 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 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 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 raw_json() noexcept; - - protected: - /** - * Go to the end of the object, no matter where you are right now. - */ - simdjson_inline error_code consume() noexcept; - static simdjson_inline simdjson_result start(value_iterator& iter) noexcept; - static simdjson_inline simdjson_result start_root(value_iterator& iter) noexcept; - static simdjson_inline simdjson_result 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{}; - - friend class value; - friend class document; - friend struct simdjson_result; - }; - - } // namespace ondemand - } // namespace haswell -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public haswell::implementation_simdjson_result_base { - 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 begin() noexcept; - simdjson_inline simdjson_result end() noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) && noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - - inline simdjson_result reset() noexcept; - inline simdjson_result is_empty() noexcept; - inline simdjson_result count_fields() & noexcept; - inline simdjson_result raw_json() noexcept; - - }; - -} // 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; - - // - // Iterator interface - // - - // Reads key and value, yielding them to the user. - // MUST ONLY BE CALLED ONCE PER ITERATION. - simdjson_inline simdjson_result 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 ',' - simdjson_inline object_iterator& operator++() noexcept; - - private: - /** - * 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; - friend class object; - }; - - } // namespace ondemand - } // namespace haswell -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public haswell::implementation_simdjson_result_base { - 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 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&) const noexcept; - // Assumes it's being compared with the end. true if depth >= iter->depth. - simdjson_inline bool operator!=(const simdjson_result&) const noexcept; - // Checks for ']' and ',' - simdjson_inline simdjson_result& 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/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 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 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 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 to_json_string(haswell::ondemand::array& x) noexcept; - inline simdjson_result to_json_string(simdjson_result x); - inline simdjson_result to_json_string(simdjson_result x); - inline simdjson_result to_json_string(simdjson_result x); - inline simdjson_result to_json_string(simdjson_result x); -} // namespace simdjson +struct has_custom_serialization : std::false_type {}; +#endif // SIMDJSON_SUPPORTS_CONCEPTS +namespace haswell { +namespace builder { /** - * 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 + * 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. */ -namespace simdjson { - namespace haswell { - namespace ondemand { +class string_builder { +public: + simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY); - /** - * 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); + 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::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 + 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 + simdjson_inline void append_key_value(key_type key, value_type value) noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + template + simdjson_inline void append_key_value(value_type value) noexcept; + + // Support for optional types (std::optional, etc.) + template + requires(!require_custom_serialization) + simdjson_inline void append(const T &opt); + + template + requires(require_custom_serialization) + simdjson_inline void append(T &&val); + + // Support for string-like types + template + requires(std::is_convertible::value || + std::is_same::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 +requires (!std::is_convertible::value && !require_custom_serialization) + 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; #if SIMDJSON_EXCEPTIONS - inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result 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 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&& 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&& 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 x); -#endif - } - } -} // namespace simdjson::haswell::ondemand - -#endif // SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H -/* end file simdjson/generic/ondemand/serialization.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/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::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::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::started(value_iterator& iter) noexcept { - bool has_value; - SIMDJSON_TRY(iter.started_array().get(has_value)); - return array(iter); - } - - simdjson_inline simdjson_result array::begin() noexcept { -#if SIMDJSON_DEVELOPMENT_CHECKS - if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } -#endif - return array_iterator(iter); - } - simdjson_inline simdjson_result array::end() noexcept { - return array_iterator(iter); - } - 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 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(starting_point), size_t(final_point - starting_point)); - } - - SIMDJSON_PUSH_DISABLE_WARNINGS - SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING - simdjson_inline simdjson_result array::count_elements() & 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 array after counting the number of elements. - iter.reset_array(); - return count; - } - SIMDJSON_POP_DISABLE_WARNINGS - - simdjson_inline simdjson_result array::is_empty() & noexcept { - bool is_not_empty; - auto error = iter.reset_array().get(is_not_empty); - if (error) { return error; } - return !is_not_empty; - } - - inline simdjson_result array::reset() & noexcept { - return iter.reset_array(); - } - - inline simdjson_result array::at_pointer(std::string_view json_pointer) noexcept { - if (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; } - - // Read the array index - size_t array_index = 0; - size_t i; - for (i = 0; i < json_pointer.length() && json_pointer[i] != '/'; i++) { - uint8_t digit = uint8_t(json_pointer[i] - '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; } - array_index = array_index * 10 + digit; - } - - // 0 followed by other digits is invalid - if (i > 1 && json_pointer[0] == '0') { return INVALID_JSON_POINTER; } // "JSON pointer array index has other characters after 0" - - // Empty string is invalid; so is a "/" with no digits before it - if (i == 0) { return INVALID_JSON_POINTER; } // "Empty string in JSON pointer array index" - // 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 std::string json_path_to_pointer_conversion(std::string_view json_path) { - if (json_path.empty() || (json_path.front() != '.' && - json_path.front() != '[')) { - return "-1"; // This is just a sentinel value, the caller should check for this and return an error. - } - - std::string result; - // Reserve space to reduce allocations, adjusting for potential increases due - // to escaping. - result.reserve(json_path.size() * 2); - - size_t i = 0; - - while (i < json_path.length()) { - if (json_path[i] == '.') { - result += '/'; - } - else if (json_path[i] == '[') { - result += '/'; - ++i; // Move past the '[' - while (i < json_path.length() && json_path[i] != ']') { - if (json_path[i] == '~') { - result += "~0"; - } - else if (json_path[i] == '/') { - result += "~1"; - } - else { - result += json_path[i]; - } - ++i; - } - if (i == json_path.length() || json_path[i] != ']') { - return "-1"; // Using sentinel value that will be handled as an error by the caller. - } - } - else { - if (json_path[i] == '~') { - result += "~0"; - } - else if (json_path[i] == '/') { - result += "~1"; - } - else { - result += json_path[i]; - } - } - ++i; - } - - return result; - } - - inline simdjson_result array::at_path(std::string_view json_path) noexcept { - auto json_pointer = json_path_to_pointer_conversion(json_path); - if (json_pointer == "-1") { return INVALID_JSON_POINTER; } - return at_pointer(json_pointer); - } - - simdjson_inline simdjson_result array::at(size_t index) noexcept { - size_t i = 0; - for (auto value : *this) { - if (i == index) { return value; } - i++; - } - return INDEX_OUT_OF_BOUNDS; - } - - } // namespace ondemand - } // namespace haswell -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result( - haswell::ondemand::array&& value - ) noexcept - : implementation_simdjson_result_base( - std::forward(value) - ) - { - } - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept - : implementation_simdjson_result_base(error) - { - } - - simdjson_inline simdjson_result simdjson_result::begin() noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() noexcept { - if (error()) { return error(); } - return first.end(); - } - simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); - } - simdjson_inline simdjson_result simdjson_result::is_empty() & noexcept { - if (error()) { return error(); } - return first.is_empty(); - } - simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } - simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { return error(); } - return first.at_path(json_path); - } - simdjson_inline simdjson_result simdjson_result::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 } - {} - - simdjson_inline simdjson_result array_iterator::operator*() noexcept { - 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 { - 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; - } - - } // namespace ondemand - } // namespace haswell -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result( - haswell::ondemand::array_iterator&& value - ) noexcept - : haswell::implementation_simdjson_result_base(std::forward(value)) - { - first.iter.assert_is_valid(); - } - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : haswell::implementation_simdjson_result_base({}, error) - { - } - - simdjson_inline simdjson_result simdjson_result::operator*() noexcept { - if (error()) { return error(); } - return *first; - } - simdjson_inline bool simdjson_result::operator==(const simdjson_result& other) const noexcept { - if (!first.iter.is_valid()) { return !error(); } - return first == other.first; - } - simdjson_inline bool simdjson_result::operator!=(const simdjson_result& other) const noexcept { - if (!first.iter.is_valid()) { return error(); } - return first != other.first; - } - simdjson_inline simdjson_result& simdjson_result::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_ARRAY_ITERATOR_INL_H -/* end file simdjson/generic/ondemand/array_iterator-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/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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -namespace simdjson { - namespace haswell { - namespace ondemand { - - simdjson_inline document::document(ondemand::json_iterator&& _iter) noexcept - : iter{ std::forward(_iter) } - { - logger::log_start_value(iter, "document"); - } - - simdjson_inline document document::start(json_iterator&& iter) noexcept { - return document(std::forward(iter)); - } - - inline void document::rewind() noexcept { - iter.rewind(); - } - - inline std::string document::to_debug_string() noexcept { - return iter.to_string(); - } - - inline simdjson_result 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 document::start_or_resume_object() noexcept { - if (iter.at_root()) { - return get_object(); - } - else { - return object::resume(resume_value_iterator()); - } - } - simdjson_inline simdjson_result document::get_value() noexcept { - // Make sure we start any arrays or objects before returning, so that start_root_() - // 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 document::get_array() & noexcept { - auto value = get_root_value_iterator(); - return array::start_root(value); - } - simdjson_inline simdjson_result 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 document::get_uint64() noexcept { - return get_root_value_iterator().get_root_uint64(true); - } - simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { - return get_root_value_iterator().get_root_uint64_in_string(true); - } - simdjson_inline simdjson_result document::get_int64() noexcept { - return get_root_value_iterator().get_root_int64(true); - } - simdjson_inline simdjson_result document::get_int64_in_string() noexcept { - return get_root_value_iterator().get_root_int64_in_string(true); - } - simdjson_inline simdjson_result document::get_double() noexcept { - return get_root_value_iterator().get_root_double(true); - } - simdjson_inline simdjson_result document::get_double_in_string() noexcept { - return get_root_value_iterator().get_root_double_in_string(true); - } - simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { - return get_root_value_iterator().get_root_string(true, allow_replacement); - } - template - 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 document::get_wobbly_string() noexcept { - return get_root_value_iterator().get_root_wobbly_string(true); - } - simdjson_inline simdjson_result document::get_raw_json_string() noexcept { - return get_root_value_iterator().get_root_raw_json_string(true); - } - simdjson_inline simdjson_result document::get_bool() noexcept { - return get_root_value_iterator().get_root_bool(true); - } - simdjson_inline simdjson_result document::is_null() noexcept { - return get_root_value_iterator().is_root_null(true); - } - - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } - - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_value(); } - - template simdjson_inline error_code document::get(T& out) & noexcept { - return get().get(out); - } - template simdjson_deprecated simdjson_inline error_code document::get(T& out) && noexcept { - return std::forward(*this).get().get(out); - } - -#if SIMDJSON_EXCEPTIONS - template - simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } - template - simdjson_inline document::operator T() & noexcept(false) { return get(); } - 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) { return get_string(false); } - simdjson_inline document::operator raw_json_string() noexcept(false) { 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 document::count_elements() & noexcept { - auto a = get_array(); - simdjson_result 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 document::count_fields() & noexcept { - auto a = get_object(); - simdjson_result 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 document::at(size_t index) & noexcept { - auto a = get_array(); - return a.at(index); - } - simdjson_inline simdjson_result document::begin() & noexcept { - return get_array().begin(); - } - simdjson_inline simdjson_result document::end() & noexcept { - return {}; - } - - simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { - return start_or_resume_object().find_field(key); - } - simdjson_inline simdjson_result document::find_field(const char* key) & noexcept { - return start_or_resume_object().find_field(key); - } - simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { - return start_or_resume_object().find_field_unordered(key); - } - simdjson_inline simdjson_result document::find_field_unordered(const char* key) & noexcept { - return start_or_resume_object().find_field_unordered(key); - } - simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { - return start_or_resume_object()[key]; - } - simdjson_inline simdjson_result document::operator[](const char* key) & noexcept { - return start_or_resume_object()[key]; - } - - simdjson_inline error_code document::consume() noexcept { - auto error = iter.skip_child(0); - if (error) { iter.abandon(); } - return error; - } - - simdjson_inline simdjson_result 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(starting_point), size_t(final_point - starting_point)); - } - - simdjson_inline simdjson_result document::type() noexcept { - return get_root_value_iterator().type(); - } - - simdjson_inline simdjson_result document::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 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 document::is_integer() noexcept { - return get_root_value_iterator().is_root_integer(true); - } - - simdjson_inline simdjson_result document::get_number_type() noexcept { - return get_root_value_iterator().get_root_number_type(true); - } - - simdjson_inline simdjson_result document::get_number() noexcept { - return get_root_value_iterator().get_root_number(true); - } - - - simdjson_inline simdjson_result document::raw_json_token() noexcept { - auto _iter = get_root_value_iterator(); - return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); - } - - simdjson_inline simdjson_result 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 document::at_path(std::string_view json_path) noexcept { - rewind(); // Rewind the document each time at_pointer is called - if (json_path.empty()) { - return this->get_value(); - } - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) { - case json_type::array: - return (*this).get_array().at_path(json_path); - case json_type::object: - return (*this).get_object().at_path(json_path); - default: - return INVALID_JSON_POINTER; - } - } - - } // namespace ondemand - } // namespace haswell -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result( - haswell::ondemand::document&& value - ) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) - { - } - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept : - implementation_simdjson_result_base( - error - ) - { - } - simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); - } - simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); - } - simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; - } - simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() & noexcept { - return {}; - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char* key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](const char* key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::find_field(const char* key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { - if (error()) { return error(); } - return first.get_array(); - } - simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { - if (error()) { return error(); } - return first.get_object(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); - } - simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); - } - simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); - } - template - simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(receiver, allow_replacement); - } - simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); - } - simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); - } - simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); - } - simdjson_inline simdjson_result simdjson_result::get_value() noexcept { - if (error()) { return error(); } - return first.get_value(); - } - simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); - } - - template - simdjson_inline simdjson_result simdjson_result::get() & noexcept { - if (error()) { return error(); } - return first.get(); - } - template - simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { - if (error()) { return error(); } - return std::forward(first).get(); - } - template - simdjson_inline error_code simdjson_result::get(T& out) & noexcept { - if (error()) { return error(); } - return first.get(out); - } - template - simdjson_inline error_code simdjson_result::get(T& out) && noexcept { - if (error()) { return error(); } - return std::forward(first).get(out); - } - - template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; - template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { - if (error()) { return error(); } - return std::forward(first); - } - template<> simdjson_inline error_code simdjson_result::get(haswell::ondemand::document& out) & noexcept = delete; - template<> simdjson_inline error_code simdjson_result::get(haswell::ondemand::document& out) && noexcept { - if (error()) { return error(); } - out = std::forward(first); - return SUCCESS; - } - - simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); - } - - simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); - } - - simdjson_inline simdjson_result simdjson_result::is_string() noexcept { - if (error()) { return error(); } - return first.is_string(); - } - - simdjson_inline bool simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); - } - - simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); - } - - simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); - } - - simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); - } - - -#if SIMDJSON_EXCEPTIONS - template ::value == false>::type> - simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator haswell::ondemand::array() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator haswell::ondemand::object() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator haswell::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator haswell::ondemand::value() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } + /** + * 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 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 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; + +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); + + /** + * 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. + */ + simdjson_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 buffer{}; + size_t position{0}; + size_t capacity{0}; + bool is_valid{true}; +}; - simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); - } - simdjson_inline bool simdjson_result::at_end() const noexcept { - if (error()) { return error(); } - return first.at_end(); - } - - - simdjson_inline int32_t simdjson_result::current_depth() const noexcept { - if (error()) { return error(); } - return first.current_depth(); - } - - simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); - } - - simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } - - simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { return error(); } - return first.at_path(json_path); - } - -} // namespace simdjson - - -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 document_reference::get_array() & noexcept { return doc->get_array(); } - simdjson_inline simdjson_result 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 document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } - simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } - simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } - simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } - simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } - simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } - simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } - template - 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 document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } - simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } - simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } - simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } - simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } -#if SIMDJSON_EXCEPTIONS - template - simdjson_inline document_reference::operator T() noexcept(false) { return get(); } - 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 document_reference::count_elements() & noexcept { return doc->count_elements(); } - simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } - simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } - simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } - simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } - simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } - simdjson_inline simdjson_result document_reference::find_field(const char* key) & noexcept { return doc->find_field(key); } - simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } - simdjson_inline simdjson_result document_reference::operator[](const char* key) & noexcept { return (*doc)[key]; } - simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } - simdjson_inline simdjson_result document_reference::find_field_unordered(const char* key) & noexcept { return doc->find_field_unordered(key); } - simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } - simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } - simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } - simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } - simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } - simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } - simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } - simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } - simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } - simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } - simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } - simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } - simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json(); } - simdjson_inline document_reference::operator document& () const noexcept { return *doc; } - - } // namespace ondemand - } // namespace haswell -} // namespace simdjson - - - -namespace simdjson { - simdjson_inline simdjson_result::simdjson_result(haswell::ondemand::document_reference value, error_code error) - noexcept : implementation_simdjson_result_base(std::forward(value), error) {} - - - simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); - } - simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); - } - simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; - } - simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() & noexcept { - return {}; - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char* key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](const char* key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::find_field(const char* key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { - if (error()) { return error(); } - return first.get_array(); - } - simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { - if (error()) { return error(); } - return first.get_object(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); - } - simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); - } - simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); - } - template - simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(receiver, allow_replacement); - } - simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); - } - simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); - } - simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); - } - simdjson_inline simdjson_result simdjson_result::get_value() noexcept { - if (error()) { return error(); } - return first.get_value(); - } - simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); - } - simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); - } - simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); - } - simdjson_inline simdjson_result simdjson_result::is_string() noexcept { - if (error()) { return error(); } - return first.is_string(); - } - simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); - } - simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); - } - simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); - } - simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); - } -#if SIMDJSON_EXCEPTIONS - template ::value == false>::type> - simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator haswell::ondemand::array() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator haswell::ondemand::object() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator haswell::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator haswell::ondemand::value() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } -#endif - - simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); - } - - simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); - } - - simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } - - simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { - return error(); - } - return first.at_path(json_path); - } - -} // namespace simdjson - -#endif // SIMDJSON_GENERIC_ONDEMAND_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 -#include - -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 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 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 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 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 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 - ) 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 }, - 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 }, - allow_comma_separated{ false }, - 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 { - 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 document_stream::iterator::operator*() noexcept { - //if(stream->error) { return stream->error; } - return simdjson_result(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::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; } - doc_index = batch_start; - 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 - 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(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; - } - } - } - - inline void document_stream::next_document() noexcept { - // 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) { doc.iter.consume_character(','); } - // 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; - if (remaining <= batch_size) { - return p.implementation->stage1(&buf[_batch_start], remaining, stage1_mode::streaming_final); - } - else { - return p.implementation->stage1(&buf[_batch_start], batch_size, stage1_mode::streaming_partial); - } - } - - 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 { - auto depth = stream->doc.iter.depth(); - auto cur_struct_index = stream->doc.iter._root - stream->parser->implementation->structural_indexes.get(); - - // If at root, process the first token to determine if scalar value - if (stream->doc.iter.at_root()) { - 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 - // TODO: We could remove trailing whitespaces - // This returns a string spanning from start of value to the beginning of the next document (excluded) - { - auto next_index = 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(stream->buf) + current_index(); - while (svlen > 1 && (std::isspace(start[svlen - 1]) || start[svlen - 1] == '\0')) { - svlen--; - } - return std::string_view(start, svlen); - } - } - cur_struct_index++; - } - - while (cur_struct_index <= static_cast(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(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::simdjson_result( - error_code error - ) noexcept : - implementation_simdjson_result_base(error) - { - } - simdjson_inline simdjson_result::simdjson_result( - haswell::ondemand::document_stream&& value - ) noexcept : - implementation_simdjson_result_base( - std::forward(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 + +#if !SIMDJSON_STATIC_REFLECTION +// fallback implementation until we have static reflection +template +simdjson_warn_unused simdjson_result 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 +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_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): #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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ +#if SIMDJSON_STATIC_REFLECTION -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() {} - - simdjson_inline field::field(raw_json_string key, ondemand::value&& value) noexcept - : std::pair(key, std::forward(value)) - { - } - - simdjson_inline simdjson_result 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::start(const value_iterator& parent_iter, raw_json_string key) noexcept { - return field(key, parent_iter.child()); - } - - simdjson_inline simdjson_warn_unused simdjson_result 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 answer = first.unescape(second.iter.json_iter(), allow_replacement); - first.consume(); - return answer; - } - - template - 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)); - 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(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(first.buf), end_quote - first.buf); - } - - simdjson_inline value& field::value() & noexcept { - return second; - } - - simdjson_inline value field::value() && noexcept { - return std::forward(*this).second; - } - - } // namespace ondemand - } // namespace haswell -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result( - haswell::ondemand::field&& value - ) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) - { - } - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept : - implementation_simdjson_result_base(error) - { - } - - simdjson_inline simdjson_result simdjson_result::key() noexcept { - if (error()) { return error(); } - return first.key(); - } - - simdjson_inline simdjson_result simdjson_result::key_raw_json_token() noexcept { - if (error()) { return error(); } - return first.key_raw_json_token(); - } - - simdjson_inline simdjson_result simdjson_result::escaped_key() noexcept { - if (error()) { return error(); } - return first.escaped_key(); - } - - simdjson_inline simdjson_result simdjson_result::unescaped_key(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.unescaped_key(allow_replacement); - } - - template - simdjson_inline error_code simdjson_result::unescaped_key(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.unescaped_key(receiver, allow_replacement); - } - - simdjson_inline simdjson_result simdjson_result::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(other.token)), - parser{ other.parser }, - _string_buf_loc{ other._string_buf_loc }, - error{ other.error }, - _depth{ other._depth }, - _root{ other._root }, - _streaming{ other._streaming } - { - 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; - 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 } - - { - 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 '"': - if (*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(TAPE_ERROR, "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; - } - - 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 { -#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(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 json_iterator::current_location() const noexcept { - if (!is_alive()) { // Unrecoverable error - if (!at_root()) { - return reinterpret_cast(token.peek(-1)); - } - else { - return reinterpret_cast(token.peek()); - } - } - if (at_end()) { - return OUT_OF_BOUNDS; - } - return reinterpret_cast(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_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 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); - SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc)); - return result; -#else - return parser->unescape(in, _string_buf_loc, allow_replacement); -#endif - } - - simdjson_inline simdjson_result json_iterator::unescape_wobbly(raw_json_string in) noexcept { -#if SIMDJSON_DEVELOPMENT_CHECKS - auto result = parser->unescape_wobbly(in, _string_buf_loc); - SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc)); - 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_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_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::simdjson_result(haswell::ondemand::json_iterator&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(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; - default: SIMDJSON_UNREACHABLE(); - } - return out; - } - -#if SIMDJSON_EXCEPTIONS - inline std::ostream& operator<<(std::ostream& out, simdjson_result& 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::simdjson_result(haswell::ondemand::json_type&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(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 +#include #include +#include +#include +#include +#include +#include +#include +// #include // for std::define_static_string - header not available yet namespace simdjson { - namespace haswell { - namespace ondemand { - namespace logger { +namespace haswell { +namespace builder { - 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. +template + requires(concepts::container_but_not_string && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + auto it = t.begin(); + auto end = t.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} - // Helper to turn unprintable or newline characters into spaces - static inline char printable_char(char c) { - if (c >= 0x20) { - return c; - } - else { - return ' '; - } - } +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +constexpr void atom(string_builder &b, const T &t) { + b.escape_and_append_with_quotes(t); +} - template - 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_s); - if (size <= 0) return std::string(); - std::unique_ptr 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 - 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)...); - } - - template - 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)...); - 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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -namespace simdjson { - namespace haswell { - namespace ondemand { - - simdjson_inline simdjson_result 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(key.size()), key.data()); - return NO_SUCH_FIELD; - } - return value(iter.child()); - } - simdjson_inline simdjson_result 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(key.size()), key.data()); - return NO_SUCH_FIELD; - } - return value(iter.child()); - } - simdjson_inline simdjson_result object::operator[](const std::string_view key) & noexcept { - return find_field_unordered(key); - } - simdjson_inline simdjson_result object::operator[](const std::string_view key) && noexcept { - return std::forward(*this).find_field_unordered(key); - } - simdjson_inline simdjson_result object::find_field(const std::string_view key) & noexcept { - 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(key.size()), key.data()); - return NO_SUCH_FIELD; - } - return value(iter.child()); - } - simdjson_inline simdjson_result object::find_field(const std::string_view key) && noexcept { - 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(key.size()), key.data()); - return NO_SUCH_FIELD; - } - return value(iter.child()); - } - - simdjson_inline simdjson_result object::start(value_iterator& iter) noexcept { - SIMDJSON_TRY(iter.start_object().error()); - return object(iter); - } - simdjson_inline simdjson_result object::start_root(value_iterator& iter) noexcept { - SIMDJSON_TRY(iter.start_root_object().error()); - return object(iter); - } - 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 object::raw_json() noexcept { - 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(starting_point), size_t(final_point - starting_point)); - } - - simdjson_inline simdjson_result 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::begin() noexcept { -#if SIMDJSON_DEVELOPMENT_CHECKS - if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } -#endif - return object_iterator(iter); - } - simdjson_inline simdjson_result object::end() noexcept { - return object_iterator(iter); - } - - inline simdjson_result object::at_pointer(std::string_view json_pointer) noexcept { - if (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 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 object::at_path(std::string_view json_path) noexcept { - auto json_pointer = json_path_to_pointer_conversion(json_path); - if (json_pointer == "-1") { - return INVALID_JSON_POINTER; - } - return at_pointer(json_pointer); - } - - simdjson_inline simdjson_result object::count_fields() & noexcept { - size_t count{ 0 }; - // Important: we do not consume any of the values. - 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 object::is_empty() & noexcept { - 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 object::reset() & noexcept { - return iter.reset_object(); - } - - } // namespace ondemand - } // namespace haswell -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result(haswell::ondemand::object&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} - - simdjson_inline simdjson_result simdjson_result::begin() noexcept { - if (error()) { return error(); } - return first.begin(); +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &m) { + if (m.empty()) { + b.append_raw("{}"); + return; + } + b.append('{'); + bool first = true; + for (const auto& [key, value] : m) { + if (!first) { + b.append(','); } - simdjson_inline simdjson_result simdjson_result::end() noexcept { - if (error()) { return error(); } - return first.end(); + first = false; + // Keys must be convertible to string_view per the concept + b.escape_and_append_with_quotes(key); + b.append(':'); + atom(b, value); + } + b.append('}'); +} + + +template::value && !std::is_same_v>::type> +constexpr void atom(string_builder &b, const number_type t) { + b.append(t); +} + +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, t.[:dm:]); + i++; + }; + b.append('}'); +} + +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &opt) { + if (opt) { + atom(b, opt.value()); + } else { + b.append_raw("null"); + } +} + +// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &ptr) { + if (ptr) { + atom(b, *ptr); + } else { + b.append_raw("null"); + } +} + +// Support for enums - serialize as string representation using expand approach from P2996R12 +template + requires(std::is_enum_v && !require_custom_serialization) +void atom(string_builder &b, const T &e) { +#if SIMDJSON_STATIC_REFLECTION + 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(std::meta::identifier_of(enum_val))); + if (e == [:enum_val:]) { + b.append_raw(enum_str); + return; } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) && noexcept { - if (error()) { return error(); } - return std::forward(first).find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) && noexcept { - if (error()) { return error(); } - return std::forward(first)[key]; - } - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) && noexcept { - if (error()) { return error(); } - return std::forward(first).find_field(key); - } - - simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } - - simdjson_inline simdjson_result simdjson_result::at_path( - std::string_view json_path) noexcept { - if (error()) { - return error(); - } - return first.at_path(json_path); - } - - inline simdjson_result simdjson_result::reset() noexcept { - if (error()) { return error(); } - return first.reset(); - } - - inline simdjson_result simdjson_result::is_empty() noexcept { - if (error()) { return error(); } - return first.is_empty(); - } - - simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); - } - - simdjson_inline simdjson_result simdjson_result::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 } - {} - - simdjson_inline simdjson_result object_iterator::operator*() noexcept { - 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 { - // 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::simdjson_result( - haswell::ondemand::object_iterator&& value - ) noexcept - : implementation_simdjson_result_base(std::forward(value)) - { - first.iter.assert_is_valid(); - } - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base({}, error) - { - } - - simdjson_inline simdjson_result simdjson_result::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::operator==(const simdjson_result& 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::operator!=(const simdjson_result& other) const noexcept { - if (!first.iter.is_valid()) { return error(); } - return first != other.first; - } - // Checks for ']' and ',' - simdjson_inline simdjson_result& simdjson_result::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/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; - 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 parser::iterate(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } - - json.remove_utf8_bom(); - - // 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(json.data()), json.length(), stage1_mode::regular)); - return document::start({ reinterpret_cast(json.data()), this }); - } - -#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON - simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } - - json.remove_utf8_bom(); - - // 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(json.data()), json.length(), stage1_mode::regular); - if (err) { - if (err != UNCLOSED_STRING) - return err; - } - return document::start({ reinterpret_cast(json.data()), this, true }); - } -#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON - - simdjson_warn_unused simdjson_inline simdjson_result 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 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 parser::iterate(std::string_view json, size_t allocated) & noexcept { - return iterate(padded_string_view(json, allocated)); - } - - simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(std::string& json) & noexcept { - if (json.capacity() - json.size() < SIMDJSON_PADDING) { - json.reserve(json.size() + SIMDJSON_PADDING); - } - return iterate(padded_string_view(json)); - } - - simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const std::string& json) & noexcept { - return iterate(padded_string_view(json)); - } - - simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const simdjson_result& result) & noexcept { - // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 parser::iterate(const simdjson_result& result) & noexcept { - // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 parser::iterate_raw(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_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(json.data()), json.length(), stage1_mode::regular)); - return json_iterator(reinterpret_cast(json.data()), this); - } - - inline simdjson_result parser::iterate_many(const uint8_t* buf, size_t len, size_t batch_size, bool allow_comma_separated) 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 (allow_comma_separated && batch_size < len) { batch_size = len; } - return document_stream(*this, buf, len, batch_size, allow_comma_separated); - } - inline simdjson_result parser::iterate_many(const char* buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept { - return iterate_many(reinterpret_cast(buf), len, batch_size, allow_comma_separated); - } - inline simdjson_result parser::iterate_many(const std::string& s, size_t batch_size, bool allow_comma_separated) noexcept { - return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated); - } - inline simdjson_result parser::iterate_many(const padded_string& s, size_t batch_size, bool allow_comma_separated) noexcept { - return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated); - } - - 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 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(dst), end - dst); - dst = end; - return result; - } - - simdjson_inline simdjson_warn_unused simdjson_result 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(dst), end - dst); - dst = end; - return result; - } - - } // namespace ondemand - } // namespace haswell -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result(haswell::ondemand::parser&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(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(buf); } - - - simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept { - size_t pos{ 0 }; - // if the content has no escape character, just scan through it quickly! - for (; pos < target.size() && target[pos] != '\\'; pos++) {} - // slow path may begin. - bool escaping{ false }; - for (; pos < target.size(); pos++) { - if ((target[pos] == '"') && !escaping) { - return false; - } - else if (target[pos] == '\\') { - escaping = !escaping; - } - else { - escaping = false; - } - } - return true; - } - - simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* target) noexcept { - size_t pos{ 0 }; - // if the content has no escape character, just scan through it quickly! - for (; target[pos] && target[pos] != '\\'; pos++) {} - // slow path may begin. - bool escaping{ false }; - for (; target[pos]; pos++) { - if ((target[pos] == '"') && !escaping) { - return false; - } - else if (target[pos] == '\\') { - escaping = !escaping; - } - else { - escaping = false; - } - } - 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()); - } - const char* r{ raw() }; - size_t pos{ 0 }; - for (; pos < target.size(); pos++) { - if (r[pos] != target[pos]) { return false; } - } - if (r[pos] != '"') { return false; } - return true; - } - - 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 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 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::simdjson_result(haswell::ondemand::raw_json_string&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} - - simdjson_inline simdjson_result simdjson_result::raw() const noexcept { - if (error()) { return error(); } - return first.raw(); - } - simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::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 simdjson_result::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/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): #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 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 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 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 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 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 to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); - } - - inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); - } - - inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); - } - - inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); - } - - inline simdjson_result to_json_string(simdjson_result 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 x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); - } + }; + // Fallback to integer if enum value not found + atom(b, static_cast>(e)); #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); - } - } + // Fallback: serialize as integer if reflection not available + atom(b, static_cast>(e)); #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 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&& x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); - } - inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& 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 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 - } +// Support for appendable containers that don't have operator[] (sets, etc.) +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &container) { + if (container.empty()) { + b.append_raw("[]"); + return; + } + b.append('['); + bool first = true; + for (const auto& item : container) { + if (!first) { + b.append(','); } -} // namespace simdjson::haswell::ondemand + first = false; + atom(b, item); + } + b.append(']'); +} -#endif // SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H -/* end file simdjson/generic/ondemand/serialization-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 +// append functions that delegate to atom functions for primitive types +template + requires(std::is_arithmetic_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +// works for struct +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^Z, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, z.[:dm:]); + i++; + }; + b.append('}'); +} + +// works for container that have begin() and end() iterators +template + requires(concepts::container_but_not_string && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + auto it = z.begin(); + auto end = z.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} + +template + requires (require_custom_serialization) +void append(string_builder &b, const Z &z) { + b.append(z); +} + + +template +simdjson_warn_unused simdjson_result to_json_string(const Z &z, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +template +simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + 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 +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 + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +void extract_from(string_builder &b, const T &obj) { + // 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) || ...); + }; + + b.append('{'); + bool first = true; + + // 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_public(mem)) { + 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 (should_extract(key)) { + if (!first) { + b.append(','); + } + first = false; + + // Serialize the key + constexpr auto quoted_key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem))); + b.append_raw(quoted_key); + b.append(':'); + + // Serialize the value + atom(b, obj.[:mem:]); + } + } + }; + + b.append('}'); +} + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + extract_from(b, obj); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +} // namespace builder +} // namespace haswell +// Alias the function template to 'to' in the global namespace +template +simdjson_warn_unused simdjson_result to_json(const Z &z, size_t initial_capacity = haswell::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + haswell::builder::string_builder b(initial_capacity); + haswell::builder::append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} +template +simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = haswell::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + haswell::builder::string_builder b(initial_capacity); + haswell::builder::append(b, z); + std::string_view view; + if(auto e = b.view().get(view); e) { return e; } + s.assign(view); + return SUCCESS; +} +// Global namespace function for extract_from +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result 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(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): #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): #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 */ -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 +#if SIMDJSON_STATIC_REFLECTION namespace simdjson { +namespace haswell { +namespace builder { - simdjson_inline simdjson_result::simdjson_result(haswell::ondemand::token_iterator&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} +/** + * 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 +simdjson_warn_unused simdjson_result 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 + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result 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(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 +simdjson_warn_unused simdjson_result 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 + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result 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(obj, opts, initial_capacity); +} } // 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-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 +#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 +#include +#include +#ifndef SIMDJSON_GENERIC_STRING_BUILDER_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): #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 SIMDJSON_EXPERIMENTAL_HAS_NEON +#include +#ifdef _MSC_VER +#include +#endif +#endif +#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 +#include +#ifdef _MSC_VER +#include +#endif +#endif + namespace simdjson { - namespace haswell { - namespace ondemand { +namespace haswell { +namespace builder { - 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; - } +static SIMDJSON_CONSTEXPR_LAMBDA std::array + 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}; - simdjson_inline simdjson_result value::get_array() noexcept { - return array::start(iter); - } - simdjson_inline simdjson_result value::get_object() noexcept { - return object::start(iter); - } - simdjson_inline simdjson_result value::start_or_resume_object() noexcept { - if (iter.at_start()) { - return get_object(); - } - else { - return object::resume(iter); - } - } +/** - simdjson_inline simdjson_result value::get_raw_json_string() noexcept { - return iter.get_raw_json_string(); - } - simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { - return iter.get_string(allow_replacement); - } - template - 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 value::get_wobbly_string() noexcept { - return iter.get_wobbly_string(); - } - simdjson_inline simdjson_result value::get_double() noexcept { - return iter.get_double(); - } - simdjson_inline simdjson_result value::get_double_in_string() noexcept { - return iter.get_double_in_string(); - } - simdjson_inline simdjson_result value::get_uint64() noexcept { - return iter.get_uint64(); - } - simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { - return iter.get_uint64_in_string(); - } - simdjson_inline simdjson_result value::get_int64() noexcept { - return iter.get_int64(); - } - simdjson_inline simdjson_result value::get_int64_in_string() noexcept { - return iter.get_int64_in_string(); - } - simdjson_inline simdjson_result value::get_bool() noexcept { - return iter.get_bool(); - } - simdjson_inline simdjson_result value::is_null() noexcept { - return iter.is_null(); - } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } +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). - template simdjson_inline error_code value::get(T& out) noexcept { - return get().get(out); - } +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; +} + +**/ + +SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline bool +simple_needs_escaping(std::string_view v) { + for (char c : v) { + // a table lookup is faster than a series of comparisons + if (json_quotable_character[static_cast(c)]) { + return true; + } + } + return false; +} + +#if SIMDJSON_EXPERIMENTAL_HAS_NEON +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + uint8x16_t running = vdupq_n_u8(0); + uint8x16_t v34 = vdupq_n_u8(34); + uint8x16_t v92 = vdupq_n_u8(92); + + for (; i + 15 < view.size(); i += 16) { + uint8x16_t word = vld1q_u8((const uint8_t *)view.data() + i); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + if (i < view.size()) { + uint8x16_t word = + vld1q_u8((const uint8_t *)view.data() + view.length() - 16); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + return vmaxvq_u32(vreinterpretq_u32_u8(running)) != 0; +} +#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2 +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + __m128i running = _mm_setzero_si128(); + for (; i + 15 < view.size(); i += 16) { + + __m128i word = + _mm_loadu_si128(reinterpret_cast(view.data() + i)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + if (i < view.size()) { + __m128i word = _mm_loadu_si128( + reinterpret_cast(view.data() + view.length() - 16)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + return _mm_movemask_epi8(running) != 0; +} +#else +simdjson_inline bool fast_needs_escaping(std::string_view view) { + return simple_needs_escaping(view); +} +#endif + +// 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(*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(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)); + + if (vmaxvq_u32(vreinterpretq_u32_u8(needs_escape)) != 0) { + // Found quotable character - extract exact byte position using ctz + uint64x2_t as64 = vreinterpretq_u64_u8(needs_escape); + uint64_t lo = vgetq_lane_u64(as64, 0); + uint64_t hi = vgetq_lane_u64(as64, 1); + size_t offset = ptr - reinterpret_cast(view.data()); +#ifdef _MSC_VER + unsigned long trailing_zero = 0; + if (lo != 0) { + _BitScanForward64(&trailing_zero, lo); + return offset + trailing_zero / 8; + } else { + _BitScanForward64(&trailing_zero, hi); + return offset + 8 + trailing_zero / 8; + } +#else + if (lo != 0) { + return offset + __builtin_ctzll(lo) / 8; + } else { + return offset + 8 + __builtin_ctzll(hi) / 8; + } +#endif + } + 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(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(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(view.data()); +#ifdef _MSC_VER + unsigned long trailing_zero = 0; + _BitScanForward(&trailing_zero, mask); + return offset + trailing_zero; +#else + return offset + __builtin_ctz(mask); +#endif + } + 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(); + } +} + +// 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 out - initout; +} + +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; +} + +simdjson_inline void string_builder::grow_buffer(size_t desired_capacity) { + if (!is_valid) { + return; + } + std::unique_ptr 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 { + +template ::value>::type> +simdjson_really_inline int int_log2(number_type x) { + return 63 - leading_zeroes(uint64_t(x) | 1); +} + +simdjson_really_inline int fast_digit_count_32(uint32_t x) { + static uint64_t table[] = { + 4294967296, 8589934582, 8589934582, 8589934582, 12884901788, + 12884901788, 12884901788, 17179868184, 17179868184, 17179868184, + 21474826480, 21474826480, 21474826480, 21474826480, 25769703776, + 25769703776, 25769703776, 30063771072, 30063771072, 30063771072, + 34349738368, 34349738368, 34349738368, 34349738368, 38554705664, + 38554705664, 38554705664, 41949672960, 41949672960, 41949672960, + 42949672960, 42949672960}; + return uint32_t((x + table[int_log2(x)]) >> 32); +} + +simdjson_really_inline int fast_digit_count_64(uint64_t x) { + static uint64_t table[] = {9, + 99, + 999, + 9999, + 99999, + 999999, + 9999999, + 99999999, + 999999999, + 9999999999, + 99999999999, + 999999999999, + 9999999999999, + 99999999999999, + 999999999999999ULL, + 9999999999999999ULL, + 99999999999999999ULL, + 999999999999999999ULL, + 9999999999999999999ULL}; + int y = (19 * int_log2(x) >> 6); + y += x > table[y]; + return y + 1; +} + +template ::value>::type> +simdjson_really_inline size_t digit_count(number_type v) noexcept { + static_assert(sizeof(number_type) == 8 || sizeof(number_type) == 4 || + sizeof(number_type) == 2 || sizeof(number_type) == 1, + "We only support 8-bit, 16-bit, 32-bit and 64-bit numbers"); + SIMDJSON_IF_CONSTEXPR(sizeof(number_type) <= 4) { + return fast_digit_count_32(static_cast(v)); + } + else { + return fast_digit_count_64(static_cast(v)); + } +} +static const char decimal_table[200] = { + 0x30, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35, + 0x30, 0x36, 0x30, 0x37, 0x30, 0x38, 0x30, 0x39, 0x31, 0x30, 0x31, 0x31, + 0x31, 0x32, 0x31, 0x33, 0x31, 0x34, 0x31, 0x35, 0x31, 0x36, 0x31, 0x37, + 0x31, 0x38, 0x31, 0x39, 0x32, 0x30, 0x32, 0x31, 0x32, 0x32, 0x32, 0x33, + 0x32, 0x34, 0x32, 0x35, 0x32, 0x36, 0x32, 0x37, 0x32, 0x38, 0x32, 0x39, + 0x33, 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x33, 0x34, 0x33, 0x35, + 0x33, 0x36, 0x33, 0x37, 0x33, 0x38, 0x33, 0x39, 0x34, 0x30, 0x34, 0x31, + 0x34, 0x32, 0x34, 0x33, 0x34, 0x34, 0x34, 0x35, 0x34, 0x36, 0x34, 0x37, + 0x34, 0x38, 0x34, 0x39, 0x35, 0x30, 0x35, 0x31, 0x35, 0x32, 0x35, 0x33, + 0x35, 0x34, 0x35, 0x35, 0x35, 0x36, 0x35, 0x37, 0x35, 0x38, 0x35, 0x39, + 0x36, 0x30, 0x36, 0x31, 0x36, 0x32, 0x36, 0x33, 0x36, 0x34, 0x36, 0x35, + 0x36, 0x36, 0x36, 0x37, 0x36, 0x38, 0x36, 0x39, 0x37, 0x30, 0x37, 0x31, + 0x37, 0x32, 0x37, 0x33, 0x37, 0x34, 0x37, 0x35, 0x37, 0x36, 0x37, 0x37, + 0x37, 0x38, 0x37, 0x39, 0x38, 0x30, 0x38, 0x31, 0x38, 0x32, 0x38, 0x33, + 0x38, 0x34, 0x38, 0x35, 0x38, 0x36, 0x38, 0x37, 0x38, 0x38, 0x38, 0x39, + 0x39, 0x30, 0x39, 0x31, 0x39, 0x32, 0x39, 0x33, 0x39, 0x34, 0x39, 0x35, + 0x39, 0x36, 0x39, 0x37, 0x39, 0x38, 0x39, 0x39, +}; +} // namespace internal + +template +simdjson_inline void string_builder::append(number_type v) noexcept { + static_assert(std::is_same::value || + std::is_integral::value || + std::is_floating_point::value, + "Unsupported number type"); + // If C++17 is available, we can 'if constexpr' here. + SIMDJSON_IF_CONSTEXPR(std::is_same::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::value) { + // Process 4 digits at a time instead of 2, reducing store operations + // and divisions by approximately half for large numbers. + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + unsigned_type pv = static_cast(v); + size_t dc = internal::digit_count(pv); + char *write_pointer = buffer.get() + position + dc - 1; + + // Process 4 digits per iteration for large numbers + while (pv >= 10000) { + unsigned_type q = pv / 10000; + unsigned_type r = pv % 10000; + unsigned_type r_hi = r / 100; // High 2 digits of remainder + unsigned_type r_lo = r % 100; // Low 2 digits of remainder + // Write low 2 digits first (rightmost), then high 2 digits + memcpy(write_pointer - 1, &internal::decimal_table[r_lo * 2], 2); + memcpy(write_pointer - 3, &internal::decimal_table[r_hi * 2], 2); + write_pointer -= 4; + pv = q; + } + + // Handle remaining 1-4 digits with original 2-digit loop + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_integral::value) { + // Same 4-digit batching as unsigned path for signed integers + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + bool negative = v < 0; + unsigned_type pv = static_cast(v); + if (negative) { + pv = 0 - pv; // the 0 is for Microsoft + } + size_t dc = internal::digit_count(pv); + // by always writing the minus sign, we avoid the branch. + buffer.get()[position] = '-'; + position += negative ? 1 : 0; + char *write_pointer = buffer.get() + position + dc - 1; + + // Process 4 digits per iteration for large numbers + while (pv >= 10000) { + unsigned_type q = pv / 10000; + unsigned_type r = pv % 10000; + unsigned_type r_hi = r / 100; + unsigned_type r_lo = r % 100; + memcpy(write_pointer - 1, &internal::decimal_table[r_lo * 2], 2); + memcpy(write_pointer - 3, &internal::decimal_table[r_hi * 2], 2); + write_pointer -= 4; + pv = q; + } + + // Handle remaining 1-4 digits + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_floating_point::value) { + constexpr size_t max_number_size = 24; + if (capacity_check(max_number_size)) { + // 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. + 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. + 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 +simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept { + escape_and_append_with_quotes(constevalutil::string_constant::value); +} +#endif + +simdjson_inline void string_builder::append_raw(const char *c) noexcept { + size_t len = std::strlen(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; + } +} +#if SIMDJSON_SUPPORTS_CONCEPTS +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +simdjson_inline void string_builder::append(const T &opt) { + if (opt) { + append(*opt); + } else { + append_null(); + } +} + +template + requires(require_custom_serialization) +simdjson_inline void string_builder::append(T &&val) { + serialize(*this, std::forward(val)); +} + +template + requires(std::is_convertible::value || + std::is_same::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 + requires(!std::is_convertible::value && !require_custom_serialization) +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>) { + 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 - template - simdjson_inline value::operator T() noexcept(false) { - return get(); - } - 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(); - } +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 value::begin() & noexcept { - return get_array().begin(); - } - simdjson_inline simdjson_result value::end() & noexcept { - return {}; - } - simdjson_inline simdjson_result value::count_elements() & noexcept { - simdjson_result 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 value::count_fields() & noexcept { - simdjson_result answer; - auto a = get_object(); - answer = a.count_fields(); - iter.move_at_start(); - return answer; - } - simdjson_inline simdjson_result value::at(size_t index) noexcept { - auto a = get_array(); - return a.at(index); - } +simdjson_inline simdjson_result +string_builder::view() const noexcept { + if (!is_valid) { + return simdjson::OUT_OF_CAPACITY; + } + return std::string_view(buffer.get(), position); +} - simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { - return start_or_resume_object().find_field(key); - } - simdjson_inline simdjson_result value::find_field(const char* key) noexcept { - return start_or_resume_object().find_field(key); - } +simdjson_inline simdjson_result string_builder::c_str() noexcept { + if (capacity_check(1)) { + buffer.get()[position] = '\0'; + return buffer.get(); + } + return simdjson::OUT_OF_CAPACITY; +} - simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { - return start_or_resume_object().find_field_unordered(key); - } - simdjson_inline simdjson_result value::find_field_unordered(const char* key) noexcept { - return start_or_resume_object().find_field_unordered(key); - } +simdjson_inline bool string_builder::validate_unicode() const noexcept { + return simdjson::validate_utf8(buffer.get(), position); +} - simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { - return start_or_resume_object()[key]; - } - simdjson_inline simdjson_result value::operator[](const char* key) noexcept { - return start_or_resume_object()[key]; - } +simdjson_inline void string_builder::start_object() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '{'; + } +} - simdjson_inline simdjson_result value::type() noexcept { - return iter.type(); - } +simdjson_inline void string_builder::end_object() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '}'; + } +} - simdjson_inline simdjson_result 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 void string_builder::start_array() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '['; + } +} - simdjson_inline simdjson_result value::is_string() noexcept { - json_type this_type; - auto error = type().get(this_type); - if (error) { return error; } - return (this_type == json_type::string); - } +simdjson_inline 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 bool value::is_negative() noexcept { - return iter.is_negative(); - } +simdjson_inline void string_builder::append_colon() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = ':'; + } +} - simdjson_inline simdjson_result value::is_integer() noexcept { - return iter.is_integer(); - } - simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { - return iter.get_number_type(); - } - simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { - return iter.get_number(); - } +template +simdjson_inline void +string_builder::append_key_value(key_type key, value_type value) noexcept { + static_assert(std::is_same::value || + std::is_convertible::value, + "Unsupported key type"); + escape_and_append_with_quotes(key); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(value); + } +} - simdjson_inline std::string_view value::raw_json_token() noexcept { - return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); - } +#if SIMDJSON_SUPPORTS_CONCEPTS +template +simdjson_inline void +string_builder::append_key_value(value_type value) noexcept { + escape_and_append_with_quotes(); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(value); + } +} +#endif - simdjson_inline simdjson_result 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 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::at_pointer(std::string_view json_pointer) noexcept { - 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::at_path(std::string_view json_path) noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) { - case json_type::array: - return (*this).get_array().at_path(json_path); - case json_type::object: - return (*this).get_object().at_path(json_path); - default: - return INVALID_JSON_POINTER; - } - } - - } // namespace ondemand - } // namespace haswell +} // namespace builder +} // namespace haswell } // namespace simdjson -namespace simdjson { +#endif // SIMDJSON_GENERIC_STRING_BUILDER_INL_H +/* end file simdjson/generic/builder/json_string_builder-inl.h for haswell */ - simdjson_inline simdjson_result::simdjson_result( - haswell::ondemand::value&& value - ) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) - { - } - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept : - implementation_simdjson_result_base(error) - { - } - simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); - } - simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); - } - simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() & noexcept { - if (error()) { return error(); } - return {}; - } - - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::find_field(const char* key) noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char* key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](const char* key) noexcept { - if (error()) { return error(); } - return first[key]; - } - - simdjson_inline simdjson_result simdjson_result::get_array() noexcept { - if (error()) { return error(); } - return first.get_array(); - } - simdjson_inline simdjson_result simdjson_result::get_object() noexcept { - if (error()) { return error(); } - return first.get_object(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); - } - simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); - } - simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); - } - template - simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(receiver, allow_replacement); - } - simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); - } - simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); - } - simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); - } - simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); - } - - template simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return first.get(); - } - template simdjson_inline error_code simdjson_result::get(T& out) noexcept { - if (error()) { return error(); } - return first.get(out); - } - - template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return std::move(first); - } - template<> simdjson_inline error_code simdjson_result::get(haswell::ondemand::value& out) noexcept { - if (error()) { return error(); } - out = first; - return SUCCESS; - } - - simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); - } - simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); - } - simdjson_inline simdjson_result simdjson_result::is_string() noexcept { - if (error()) { return error(); } - return first.is_string(); - } - simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); - } - simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); - } - simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); - } - simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); - } -#if SIMDJSON_EXCEPTIONS - template - simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return static_cast(first); - } - simdjson_inline simdjson_result::operator haswell::ondemand::array() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator haswell::ondemand::object() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator haswell::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } -#endif - - simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); - } - - simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { - if (error()) { return error(); } - return first.raw_json(); - } - - simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); - } - - simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { - if (error()) { return error(); } - return first.current_depth(); - } - - simdjson_inline simdjson_result simdjson_result::at_pointer( - std::string_view json_pointer) noexcept { - if (error()) { - return error(); - } - return first.at_pointer(json_pointer); - } - - simdjson_inline simdjson_result simdjson_result::at_path( - std::string_view json_path) noexcept { - if (error()) { - return error(); - } - return first.at_path(json_path); - } - -} // namespace simdjson - -#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H -/* end file simdjson/generic/ondemand/value-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 { - - 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 value_iterator::start_object() noexcept { - SIMDJSON_TRY(start_container('{', "Not an object", "object")); - return started_object(); - } - - simdjson_warn_unused simdjson_inline simdjson_result 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 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(); - 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 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 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 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 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 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_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 value_iterator::start_array() noexcept { - SIMDJSON_TRY(start_container('[', "Not an array", "array")); - return started_array(); - } - - simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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(!not_true); - } - simdjson_warn_unused simdjson_inline simdjson_result 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 value_iterator::get_string(bool allow_replacement) noexcept { - return get_raw_json_string().unescape(json_iter(), allow_replacement); - } - template - simdjson_warn_unused simdjson_inline error_code value_iterator::get_string(string_type& receiver, bool allow_replacement) noexcept { - std::string_view content; - auto err = get_string(allow_replacement).get(content); - if (err) { return err; } - receiver = content; - return SUCCESS; - } - simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_wobbly_string() noexcept { - return get_raw_json_string().unescape_wobbly(json_iter()); - } - simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_raw_json_string() noexcept { - auto json = peek_scalar("string"); - if (*json != '"') { return incorrect_type_error("Not a string"); } - advance_scalar("string"); - return raw_json_string(json + 1); - } - simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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 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 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 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 value_iterator::is_integer() noexcept { - return numberparsing::is_integer(peek_non_root_scalar("integer")); - } - simdjson_inline simdjson_result value_iterator::get_number_type() noexcept { - return numberparsing::get_number_type(peek_non_root_scalar("integer")); - } - simdjson_inline simdjson_result 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 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 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.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 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.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 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 - 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; - auto err = get_root_string(check_trailing, allow_replacement).get(content); - if (err) { return err; } - receiver = content; - return SUCCESS; - } - simdjson_warn_unused simdjson_inline simdjson_result 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 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; } - advance_scalar("string"); - return raw_json_string(json + 1); - } - simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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.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 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.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 value_iterator::get_root_bool(bool check_trailing) noexcept { - auto max_len = peek_root_length(); - auto json = peek_root_scalar("bool"); - uint8_t tmpbuf[5 + 1 + 1]; // +1 for null termination - tmpbuf[5 + 1] = '\0'; // make sure that buffer is always null terminated. - if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 5 + 1)) { return incorrect_type_error("Not a boolean"); } - auto result = parse_bool(tmpbuf); - if (result.error() == SUCCESS) { - if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; } - advance_root_scalar("bool"); - } - return result; - } - simdjson_inline simdjson_result 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"); - } - 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_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. - 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 simdjson_result value_iterator::reset_array() noexcept { - if (error()) { return error(); } - move_at_container_start(); - return started_array(); - } - - simdjson_inline simdjson_result 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 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 TAPE_ERROR; - } - } - - 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::simdjson_result(haswell::ondemand::value_iterator&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} - -} // namespace simdjson - -#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H -/* end file simdjson/generic/ondemand/value_iterator-inl.h for haswell */ -/* end file simdjson/generic/ondemand/amalgamated.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 */ @@ -63417,13 +44752,13 @@ SIMDJSON_UNTARGET_REGION #undef SIMDJSON_IMPLEMENTATION /* end file simdjson/haswell/end.h */ -#endif // SIMDJSON_HASWELL_ONDEMAND_H -/* end file simdjson/haswell/ondemand.h */ +#endif // SIMDJSON_HASWELL_BUILDER_H +/* end file simdjson/haswell/builder.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/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 */ @@ -63440,14 +44775,14 @@ SIMDJSON_UNTARGET_REGION // 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 { +/** + * Implementation for Icelake (Intel AVX512). + */ +namespace icelake { - class implementation; +class implementation; - } // namespace icelake +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_ICELAKE_BASE_H @@ -63495,7 +44830,7 @@ namespace simdjson { #include #include #include // for _mm_clmulepi64_si128 - // Important: we need the AVX-512 headers: +// Important: we need the AVX-512 headers: #include #include #include @@ -63531,64 +44866,64 @@ SIMDJSON_TARGET_REGION("avx512f,avx512dq,avx512cd,avx512bw,avx512vbmi,avx512vbmi /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { - namespace { +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) { +// 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); + 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); + //////// + // 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 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)); - } +/* 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 - } +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); - } +simdjson_inline long long int count_ones(uint64_t input_num) { + return _popcnt64(input_num); +} #endif - simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, - uint64_t* result) { +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { #if SIMDJSON_REGULAR_VISUAL_STUDIO - return _addcarry_u64(0, value1, value2, - reinterpret_cast(result)); + return _addcarry_u64(0, value1, value2, + reinterpret_cast(result)); #else - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); #endif - } +} - } // unnamed namespace - } // namespace icelake +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_ICELAKE_BITMANIPULATION_H @@ -63604,24 +44939,24 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { - namespace { +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); - } +// +// 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 +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_ICELAKE_BITMASK_H @@ -63649,354 +44984,358 @@ namespace simdjson { * 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)); + 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 { +namespace icelake { +namespace { +namespace simd { - // Forward-declared so they can be used by splat and friends. - template - struct base { - __m512i value; + // Forward-declared so they can be used by splat and friends. + template + struct base { + __m512i value; - // Zero constructor - simdjson_inline base() : value{ __m512i() } {} + // Zero constructor + simdjson_inline base() : value{__m512i()} {} - // Conversion from SIMD register - simdjson_inline base(const __m512i _value) : value(_value) {} + // 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; } + // 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(this); *this_cast = *this_cast | other; return *this_cast; } - simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } - simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } - }; + // 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(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; - // Forward-declared so they can be used by splat and friends. - template - struct simd8; + // Forward-declared so they can be used by splat and friends. + template + struct simd8; - template> - struct base8 : base> { - typedef uint32_t bitmask_t; - typedef uint64_t bitmask2_t; + template> + struct base8: base> { + typedef uint32_t bitmask_t; + typedef uint64_t bitmask2_t; - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m512i _value) : base>(_value) {} + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m512i _value) : base>(_value) {} - friend simdjson_really_inline uint64_t operator==(const simd8 lhs, const simd8 rhs) { - return _mm512_cmpeq_epi8_mask(lhs, rhs); - } + friend simdjson_really_inline uint64_t operator==(const simd8 lhs, const simd8 rhs) { + return _mm512_cmpeq_epi8_mask(lhs, rhs); + } + static const int SIZE = sizeof(base::value); - static const int SIZE = sizeof(base::value); + template + simdjson_inline simd8 prev(const simd8 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); + } + }; - template - simdjson_inline simd8 prev(const simd8 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: base8 { + static simdjson_inline simd8 splat(bool _value) { return _mm512_set1_epi8(uint8_t(-(!!_value))); } - // SIMD byte mask type (returned by things like eq and gt) - template<> - struct simd8 : base8 { - static simdjson_inline simd8 splat(bool _value) { return _mm512_set1_epi8(uint8_t(-(!!_value))); } + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m512i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(splat(_value)) {} + simdjson_inline bool any() const { return !!_mm512_test_epi8_mask (*this, *this); } + simdjson_inline simd8 operator~() const { return *this ^ true; } + }; - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m512i _value) : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) : base8(splat(_value)) {} - simdjson_inline bool any() const { return !!_mm512_test_epi8_mask(*this, *this); } - simdjson_inline simd8 operator~() const { return *this ^ true; } - }; + template + struct base8_numeric: base8 { + static simdjson_inline simd8 splat(T _value) { return _mm512_set1_epi8(_value); } + static simdjson_inline simd8 zero() { return _mm512_setzero_si512(); } + static simdjson_inline simd8 load(const T values[64]) { + return _mm512_loadu_si512(reinterpret_cast(values)); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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( + 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 + ); + } - template - struct base8_numeric : base8 { - static simdjson_inline simd8 splat(T _value) { return _mm512_set1_epi8(_value); } - static simdjson_inline simd8 zero() { return _mm512_setzero_si512(); } - static simdjson_inline simd8 load(const T values[64]) { - return _mm512_loadu_si512(reinterpret_cast(values)); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdjson_inline simd8 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( - 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() {} + simdjson_inline base8_numeric(const __m512i _value) : base8(_value) {} - simdjson_inline base8_numeric() : base8() {} - simdjson_inline base8_numeric(const __m512i _value) : base8(_value) {} + // Store to array + simdjson_inline void store(T dst[64]) const { return _mm512_storeu_si512(reinterpret_cast<__m512i *>(dst), *this); } - // 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 operator+(const simd8 other) const { return _mm512_add_epi8(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return _mm512_sub_epi8(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } - // Addition/subtraction are the same for signed and unsigned - simdjson_inline simd8 operator+(const simd8 other) const { return _mm512_add_epi8(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return _mm512_sub_epi8(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - // Override to distinguish from bool version - simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 lookup_table) const { + return _mm512_shuffle_epi8(lookup_table, *this); + } - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 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 compress(uint32_t mask) would be + // sensible, but the AVX ISA makes this kind of approach difficult. + template + 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 + } - // 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 compress(uint32_t mask) would be - // sensible, but the AVX ISA makes this kind of approach difficult. - template - simdjson_inline void compress(uint64_t mask, L* output) const { - _mm512_mask_compressstoreu_epi8(output, ~mask, *this); - } + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; - template - simdjson_inline simd8 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::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - }; + // Signed bytes + template<> + struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m512i _value) : base8_numeric(_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 + )) {} - // Signed bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m512i _value) : base8_numeric(_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 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( + 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 + ); + } - // Repeat 16 values as many times as necessary (usually for lookup tables) - simdjson_inline static simd8 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( - 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 max_val(const simd8 other) const { return _mm512_max_epi8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm512_min_epi8(*this, other); } - // Order-sensitive comparisons - simdjson_inline simd8 max_val(const simd8 other) const { return _mm512_max_epi8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm512_min_epi8(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return _mm512_maskz_abs_epi8(_mm512_cmpgt_epi8_mask(*this, other),_mm512_set1_epi8(uint8_t(0x80))); } + simdjson_inline simd8 operator<(const simd8 other) const { return _mm512_maskz_abs_epi8(_mm512_cmpgt_epi8_mask(other, *this),_mm512_set1_epi8(uint8_t(0x80))); } + }; - simdjson_inline simd8 operator>(const simd8 other) const { return _mm512_maskz_abs_epi8(_mm512_cmpgt_epi8_mask(*this, other), _mm512_set1_epi8(uint8_t(0x80))); } - simdjson_inline simd8 operator<(const simd8 other) const { return _mm512_maskz_abs_epi8(_mm512_cmpgt_epi8_mask(other, *this), _mm512_set1_epi8(uint8_t(0x80))); } - }; + // Unsigned bytes + template<> + struct simd8: base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m512i _value) : base8_numeric(_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 + )) {} - // Unsigned bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m512i _value) : base8_numeric(_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 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( + 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 + ); + } - // Repeat 16 values as many times as necessary (usually for lookup tables) - simdjson_inline static simd8 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( - 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 saturating_add(const simd8 other) const { return _mm512_adds_epu8(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm512_subs_epu8(*this, other); } - // Saturated math - simdjson_inline simd8 saturating_add(const simd8 other) const { return _mm512_adds_epu8(*this, other); } - simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm512_subs_epu8(*this, other); } + // Order-specific operations + simdjson_inline simd8 max_val(const simd8 other) const { return _mm512_max_epu8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm512_min_epu8(other, *this); } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } + simdjson_inline uint64_t operator<=(const simd8 other) const { return other.max_val(*this) == other; } + simdjson_inline uint64_t operator>=(const simd8 other) const { return other.min_val(*this) == other; } + simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } - // Order-specific operations - simdjson_inline simd8 max_val(const simd8 other) const { return _mm512_max_epu8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm512_min_epu8(other, *this); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } - simdjson_inline uint64_t operator<=(const simd8 other) const { return other.max_val(*this) == other; } - simdjson_inline uint64_t operator>=(const simd8 other) const { return other.min_val(*this) == other; } - simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { return _mm512_mask_blend_epi8(*this == uint8_t(0), _mm512_set1_epi8(0), _mm512_set1_epi8(-1)); } + simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } + simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } + simdjson_inline simd8 any_bits_set(simd8 bits) const { return ~this->bits_not_set(bits); } - // Bit-specific operations - simdjson_inline simd8 bits_not_set() const { return _mm512_mask_blend_epi8(*this == uint8_t(0), _mm512_set1_epi8(0), _mm512_set1_epi8(-1)); } - simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } - simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } - simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return !_mm512_test_epi8_mask(*this, bits); } + simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } + template + simdjson_inline simd8 shr() const { return simd8(_mm512_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } + template + simdjson_inline simd8 shl() const { return simd8(_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 + simdjson_inline uint64_t get_bit() const { return _mm512_movepi8_mask(_mm512_slli_epi16(*this, 7-N)); } + }; - 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 bits) const { return !_mm512_test_epi8_mask(*this, bits); } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } - template - simdjson_inline simd8 shr() const { return simd8(_mm512_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } - template - simdjson_inline simd8 shl() const { return simd8(_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 - simdjson_inline uint64_t get_bit() const { return _mm512_movepi8_mask(_mm512_slli_epi16(*this, 7 - N)); } - }; + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 1, "Icelake kernel should use one register per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 1, "Icelake kernel should use one register per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1) : chunks{chunk0, chunk1} {} + simdjson_inline simd8x64(const simd8 chunk0) : chunks{chunk0} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr)} {} - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1) : chunks{ chunk0, chunk1 } {} - simdjson_inline simd8x64(const simd8 chunk0) : chunks{ chunk0 } {} - simdjson_inline simd8x64(const T ptr[64]) : chunks{ simd8::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 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)*0); + } - simdjson_inline void store(T ptr[64]) const { - this->chunks[0].store(ptr + sizeof(simd8) * 0); - } + simdjson_inline simd8 reduce_or() const { + return this->chunks[0]; + } - simdjson_inline simd8 reduce_or() const { - return this->chunks[0]; - } + simdjson_inline simd8x64 bit_or(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] | mask + ); + } - simdjson_inline simd8x64 bit_or(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] | mask - ); - } + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return this->chunks[0] == mask; + } - simdjson_inline uint64_t eq(const T m) const { - const simd8 mask = simd8::splat(m); - return this->chunks[0] == mask; - } + simdjson_inline uint64_t eq(const simd8x64 &other) const { + return this->chunks[0] == other.chunks[0]; + } - simdjson_inline uint64_t eq(const simd8x64& other) const { - return this->chunks[0] == other.chunks[0]; - } + simdjson_inline uint64_t lteq(const T m) const { + const simd8 mask = simd8::splat(m); + return this->chunks[0] <= mask; + } + }; // struct simd8x64 - simdjson_inline uint64_t lteq(const T m) const { - const simd8 mask = simd8::splat(m); - return this->chunks[0] <= mask; - } - }; // struct simd8x64 +} // namespace simd - } // namespace simd - - } // unnamed namespace - } // namespace icelake +} // unnamed namespace +} // namespace icelake } // namespace simdjson #endif // SIMDJSON_ICELAKE_SIMD_H @@ -64013,41 +45352,70 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { - namespace { +namespace icelake { +namespace { - using namespace simd; +using namespace simd; - // Holds backslashes and quotes locations. - struct backslash_and_quote { - public: - static constexpr uint32_t BYTES_PROCESSED = 64; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t* src, uint8_t* dst); +// 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); } + 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 + 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 v(src); - // store to dest unconditionally - we can overwrite the bits we don't like later - v.store(dst); - return { - static_cast(v == '\\'), // bs_bits - static_cast(v == '"'), // quote_bits - }; - } +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 v(src); + // store to dest unconditionally - we can overwrite the bits we don't like later + v.store(dst); + return { + static_cast(v == '\\'), // bs_bits + static_cast(v == '"'), // quote_bits + }; +} - } // unnamed namespace - } // namespace icelake + + +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 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 @@ -64064,48 +45432,48 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace icelake { - namespace numberparsing { +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(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 - } +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(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; +/** @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; + // 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 + 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); + __uint128_t r = (static_cast<__uint128_t>(value1)) * value2; + answer.low = uint64_t(r); + answer.high = uint64_t(r >> 64); #endif - return answer; - } + return answer; +} - } // namespace numberparsing - } // namespace icelake +} // namespace numberparsing +} // namespace icelake } // namespace simdjson #define SIMDJSON_SWAR_NUMBER_PARSING 1 @@ -64113,10301 +45481,1674 @@ namespace simdjson { #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! +/* 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 -// 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 +/* 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_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; - - } // 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/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): #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 { - 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; +#if SIMDJSON_SUPPORTS_CONCEPTS - /** - * Denote that we're starting a document. - */ - simdjson_inline void start_document() noexcept; +namespace icelake { +namespace builder { + class string_builder; +}} - /** - * 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; +template +struct has_custom_serialization : std::false_type {}; - /** - * Tell whether the iterator is at the EOF mark - */ - simdjson_inline bool at_end() const noexcept; +inline constexpr struct serialize_tag { + template + constexpr void operator()(icelake::builder::string_builder& b, T&& obj) const{ + return tag_invoke(*this, b, std::forward(obj)); + } - /** - * 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; +} serialize{}; +template +struct has_custom_serialization(), std::declval())) +>> : std::true_type {}; - /** - * 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 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 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 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). - */ - simdjson_warn_unused simdjson_inline simdjson_result 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). - */ - simdjson_warn_unused simdjson_inline simdjson_result 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 has_next_field() noexcept; - - /** - * Get the current field's key. - */ - simdjson_warn_unused simdjson_inline simdjson_result field_key() 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 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 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 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 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). - */ - simdjson_warn_unused simdjson_inline simdjson_result 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). - */ - simdjson_warn_unused simdjson_inline simdjson_result 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 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 get_string(bool allow_replacement) noexcept; - template - simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_int64() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_double() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_bool() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_null() noexcept; - simdjson_warn_unused simdjson_inline bool is_negative() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_integer() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_number() noexcept; - - simdjson_warn_unused simdjson_inline simdjson_result get_root_string(bool check_trailing, bool allow_replacement) noexcept; - template - 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 get_root_wobbly_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_raw_json_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64_in_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_int64(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_int64_in_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_double(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_double_in_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_bool(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline bool is_root_negative() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_root_integer(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_number_type(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_number(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_root_null(bool check_trailing) noexcept; - - 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 reset_array() noexcept; - /** - * Restarts an object iteration. - * @returns Whether the object has any fields (returns false for empty). - */ - simdjson_inline simdjson_result 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 parse_null(const uint8_t* json) const noexcept; - simdjson_inline simdjson_result 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_inline error_code start_container(uint8_t start_char, const char* incorrect_type_message, const char* type) noexcept; - 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 advance_to_value() noexcept; - - simdjson_inline error_code incorrect_type_error(const char* message) const noexcept; - 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; - /** @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_inline error_code report_error(error_code error, const char* message) noexcept; - - friend class document; - friend class object; - friend class array; - friend class value; - friend class field; - }; // value_iterator - - } // namespace ondemand - } // namespace icelake -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public icelake::implementation_simdjson_result_base { - 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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -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, uint64_t, int64_t, double, bool - * - * You may use get_double(), get_bool(), get_uint64(), get_int64(), - * 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 simdjson_inline simdjson_result get() noexcept { - // 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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " 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."); - } - - /** - * Get this value as the given type. - * - * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool - * - * @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 simdjson_inline error_code get(T& out) noexcept; - - /** - * 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 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 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 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 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 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 get_int64_in_string() 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 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 get_double_in_string() noexcept; - - /** - * Cast this JSON value to a string. - * - * The string is guaranteed to be valid UTF-8. - * - * Equivalent to get(). - * - * Important: a value should be consumed once. Calling get_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 get_string(bool allow_replacement = false) noexcept; - - /** - * 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 - 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 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 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 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 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 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 - 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 signed 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(). - * - * @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 begin() & noexcept; - /** - * Sentinel representing the end of the array. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result 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 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 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 at(size_t index) noexcept; - /** - * Look up a field by name on an object (order-sensitive). - * - * 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 }`: - * - * ```c++ - * 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 find_field(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ - simdjson_inline simdjson_result 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. - * - * It is the default, however, 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 find_field_unordered(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result find_field_unordered(const char* key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](const char* key) noexcept; - - /** - * 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 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 is_scalar() noexcept; - /** - * Checks whether the value is a string. - * - * @returns true if the type is string - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". - */ - simdjson_inline simdjson_result is_string() noexcept; - - /** - * Checks whether the value is a negative number. - * - * @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 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 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 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 raw_json() noexcept; - - /** - * Returns the current location in the document if in bounds. - */ - simdjson_inline simdjson_result 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 at_pointer(std::string_view json_pointer) noexcept; - - /** - * Get the value associated with the given JSONPath expression. We only support - * JSONPath queries that trivially convertible to JSON Pointer queries: key - * names and array indices. - * - * @return The value associated with the given JSONPath expression, or: - * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - */ - simdjson_inline simdjson_result at_path(std::string_view at_path) noexcept; - - - protected: - /** - * Create a value. - */ - 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 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; - friend struct simdjson_result; - friend class field; - }; - - } // namespace ondemand - } // namespace icelake -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public icelake::implementation_simdjson_result_base { - 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 get_array() noexcept; - simdjson_inline simdjson_result get_object() noexcept; - - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result is_null() noexcept; - - template simdjson_inline simdjson_result get() noexcept; - - template simdjson_inline error_code get(T& out) noexcept; - -#if SIMDJSON_EXCEPTIONS - template - 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 count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - - /** - * Look up a field by name on an object (order-sensitive). - * - * 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 }`: - * - * ```c++ - * 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 find_field(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ - simdjson_inline simdjson_result 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. - * - * It is the default, however, 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 find_field_unordered(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result find_field_unordered(const char* key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](const char* key) noexcept; - - /** - * Get the type of this JSON value. - * - * 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 type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_string() noexcept; - simdjson_inline simdjson_result is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - - /** @copydoc simdjson_inline std::string_view value::raw_json_token() const noexcept */ - simdjson_inline simdjson_result raw_json_token() noexcept; - simdjson_inline simdjson_result raw_json() noexcept; - - /** @copydoc simdjson_inline simdjson_result current_location() noexcept */ - simdjson_inline simdjson_result current_location() noexcept; - /** @copydoc simdjson_inline int32_t current_depth() const noexcept */ - simdjson_inline simdjson_result current_depth() const noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - }; - -} // namespace simdjson - -#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; +template +constexpr bool require_custom_serialization = has_custom_serialization::value; #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 - 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 - 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 (`{` `}` `[` `]` `,` `:` `""` `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 value_iterator; - friend class object; - template - friend simdjson_inline void logger::log_line(const json_iterator& iter, const char* title_prefix, const char* title, std::string_view detail, int delta, int depth_delta, logger::log_level level, Args&&... args) noexcept; - template - 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 : public icelake::implementation_simdjson_result_base { - 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 }; - - 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; - - /** - * 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_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_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 unescape(raw_json_string in, bool allow_replacement) noexcept; - simdjson_inline simdjson_result unescape_wobbly(raw_json_string in) noexcept; - - simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept; - - 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 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 - 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 - 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 : public icelake::implementation_simdjson_result_base { - 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 { - // 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 - 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 - 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& type) noexcept(false); -#endif - - } // namespace ondemand - } // namespace icelake -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public icelake::implementation_simdjson_result_base { - 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; - - /** - * 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 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 unescape_wobbly(json_iterator& iter) const noexcept; - const uint8_t* buf{}; - friend class object; - friend class field; - friend class parser; - friend struct simdjson_result; - }; - - 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 : public icelake::implementation_simdjson_result_base { - 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 raw() const noexcept; - simdjson_inline simdjson_warn_unused simdjson_result unescape(icelake::ondemand::json_iterator& iter, bool allow_replacement) const noexcept; - simdjson_inline simdjson_warn_unused simdjson_result 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 - -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. - * - * @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 iterate(padded_string_view json) & noexcept; -#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON - simdjson_warn_unused simdjson_result iterate_allow_incomplete_json(padded_string_view json) & noexcept; -#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const char* json, size_t len, size_t capacity) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const uint8_t* json, size_t len, size_t capacity) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(std::string_view json, size_t capacity) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const std::string& json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(std::string& json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const simdjson_result& json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const simdjson_result& json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result 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 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. - * - * ### 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. - * @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 10MB, which has been a reasonable sweet spot in our tests. - * @param allow_comma_separated (defaults on false) This allows a mode where the documents are - * separated by commas instead of whitespace. It comes with a performance - * penalty because the entire document is indexed at once (and the document must be - * less than 4 GB), and there is no multithreading. In this mode, the batch_size parameter - * is effectively ignored, as it is set to at least the document size. - * @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 iterate_many(const uint8_t* buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ - inline simdjson_result iterate_many(const char* buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ - inline simdjson_result iterate_many(const std::string& s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - inline simdjson_result iterate_many(const std::string&& s, size_t batch_size, bool allow_comma_separated = false) = delete;// unsafe - /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ - inline simdjson_result iterate_many(const padded_string& s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - inline simdjson_result iterate_many(const padded_string&& s, size_t batch_size, bool allow_comma_separated = false) = delete;// unsafe - - /** @private We do not want to allow implicit conversion from C string to std::string. */ - simdjson_result iterate_many(const char* buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete; - - /** 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 }; -#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 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 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 - - private: - /** @private [for benchmarking access] The implementation to use */ - std::unique_ptr implementation{}; - size_t _capacity{ 0 }; - size_t _max_capacity; - size_t _max_depth{ DEFAULT_MAX_DEPTH }; - std::unique_ptr string_buf{}; -#if SIMDJSON_DEVELOPMENT_CHECKS - std::unique_ptr start_positions{}; -#endif - - friend class json_iterator; - friend class document_stream; - }; - - } // namespace ondemand - } // namespace icelake -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public icelake::implementation_simdjson_result_base { - 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): #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. - */ - simdjson_inline simdjson_result begin() noexcept; - /** - * Sentinel representing the end of the array. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result 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. - * - * To check that an array is empty, it is more performant to use - * the is_empty() method. - */ - simdjson_inline simdjson_result 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 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 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 at_pointer(std::string_view json_pointer) noexcept; - - /** - * Get the value associated with the given JSONPath expression. We only support - * JSONPath queries that trivially convertible to JSON Pointer queries: key - * names and array indices. - * - * https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00 - * - * @return The value associated with the given JSONPath expression, or: - * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - */ - inline simdjson_result at_path(std::string_view json_path) noexcept; - - /** - * Consumes the array and returns a string_view instance corresponding to the - * array as represented in JSON. It points inside the original document. - */ - simdjson_inline simdjson_result 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 at(size_t index) noexcept; - protected: - /** - * Go to the end of the array, no matter where you are right now. - */ - 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 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 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 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{}; - - friend class value; - friend class document; - friend struct simdjson_result; - friend struct simdjson_result; - friend class array_iterator; - }; - - } // namespace ondemand - } // namespace icelake -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public icelake::implementation_simdjson_result_base { - 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 begin() noexcept; - simdjson_inline simdjson_result end() noexcept; - inline simdjson_result count_elements() & noexcept; - inline simdjson_result is_empty() & noexcept; - inline simdjson_result reset() & noexcept; - simdjson_inline simdjson_result at(size_t index) noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - simdjson_inline simdjson_result raw_json() noexcept; - - }; - -} // 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 "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 exactly once per element. - * - ++ must be called exactly once in between each * (*, ++, *, ++, * ...) - */ - class array_iterator { - public: - /** Create a new, invalid array iterator. */ - simdjson_inline array_iterator() noexcept = default; - - // - // Iterator interface - // - - /** - * Get the current element. - * - * Part of the std::iterator interface. - */ - simdjson_inline simdjson_result 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; - - private: - value_iterator iter{}; - - simdjson_inline array_iterator(const value_iterator& iter) noexcept; - - friend class array; - friend class value; - friend struct simdjson_result; - }; - - } // namespace ondemand - } // namespace icelake -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public icelake::implementation_simdjson_result_base { - public: - 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 operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. - simdjson_inline bool operator==(const simdjson_result&) const noexcept; - simdjson_inline bool operator!=(const simdjson_result&) const noexcept; - simdjson_inline simdjson_result& operator++() 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): #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 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 get_object() & noexcept; - /** - * Cast this JSON value to an unsigned integer. - * - * @returns A signed 64-bit integer. - * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. - */ - simdjson_inline simdjson_result get_uint64() noexcept; - /** - * Cast this JSON value (inside string) to an unsigned integer. - * - * @returns A signed 64-bit integer. - * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. - */ - simdjson_inline simdjson_result 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 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 get_int64_in_string() 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 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 get_double_in_string() noexcept; - /** - * 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 get_string(bool allow_replacement = false) noexcept; - /** - * 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 - 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 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 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 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 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 is_null() noexcept; - - /** - * Get this value as the given type. - * - * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool - * - * You may use get_double(), get_bool(), get_uint64(), get_int64(), - * 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 simdjson_inline simdjson_result get() & noexcept { - // 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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " 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."); - } - /** @overload template simdjson_result get() & noexcept */ - template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept { - // 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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " 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."); - } - - /** - * Get this value as the given type. - * - * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 simdjson_inline error_code get(T& out) & noexcept; - /** @overload template error_code get(T &out) & noexcept */ - template simdjson_deprecated simdjson_inline error_code get(T& out) && noexcept; - -#if SIMDJSON_EXCEPTIONS - /** - * Cast this JSON value to an instance of type T. The programmer is responsible for - * providing an implementation of get 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 - explicit simdjson_inline operator T() & noexcept(false); - template - 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 signed 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. - * - * @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); - /** - * 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. - */ - simdjson_inline simdjson_result 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 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 at(size_t index) & noexcept; - /** - * Begin array iteration. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result begin() & noexcept; - /** - * Sentinel representing the end of the array. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result end() & noexcept; - - /** - * Look up a field by name on an object (order-sensitive). - * - * 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 }`: - * - * ```c++ - * 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 find_field(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result 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. - * - * It is the default, however, 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 find_field_unordered(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result find_field_unordered(const char* key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](const char* key) & noexcept; - - /** - * 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). - * - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". - */ - simdjson_inline simdjson_result 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 is_scalar() noexcept; - - /** - * Checks whether the document is a string. - * - * @returns true if the type is string - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". - */ - simdjson_inline simdjson_result is_string() noexcept; - - /** - * Checks whether the document is a negative number. - * - * @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 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 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 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 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 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 at_pointer(std::string_view json_pointer) noexcept; - - /** - * Get the value associated with the given JSONPath expression. We only support - * JSONPath queries that trivially convertible to JSON Pointer queries: key - * names and array indices. - * - * https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00 - * - * Key values are matched exactly, without unescaping or Unicode normalization. - * We do a byte-by-byte comparison. E.g. - * - * const padded_string json = "{\"\\u00E9\":123}"_padded; - * auto doc = parser.iterate(json); - * doc.at_path(".\\u00E9") == 123 - * doc.at_path((const char*)u8".\u00E9") returns an error (NO_SUCH_FIELD) - * - * @return The value associated with the given JSONPath expression, or: - * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - */ - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - - /** - * Consumes the document and returns a string_view instance corresponding to the - * document as represented in JSON. It points inside the original byte array containing - * the JSON document. - */ - simdjson_inline simdjson_result raw_json() noexcept; - protected: - /** - * Consumes the document. - */ - 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 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. - */ - 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 get_array() & noexcept; - simdjson_inline simdjson_result get_object() & noexcept; - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result get_value() noexcept; - - simdjson_inline simdjson_result is_null() noexcept; - template simdjson_inline simdjson_result get() & noexcept; - simdjson_inline simdjson_result raw_json() noexcept; - simdjson_inline operator document& () const noexcept; -#if SIMDJSON_EXCEPTIONS - template - explicit simdjson_inline operator T() noexcept(false); - simdjson_inline operator array() & noexcept(false); - simdjson_inline operator 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 raw_json_string() noexcept(false); - simdjson_inline operator bool() noexcept(false); - simdjson_inline operator value() noexcept(false); -#endif - simdjson_inline simdjson_result count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) & noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(const char* key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](const char* key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(const char* key) & noexcept; - - simdjson_inline simdjson_result type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_string() noexcept; - - simdjson_inline simdjson_result current_location() noexcept; - simdjson_inline int32_t current_depth() const noexcept; - simdjson_inline bool is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - simdjson_inline simdjson_result raw_json_token() noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - - private: - document* doc{ nullptr }; - }; - } // namespace ondemand - } // namespace icelake -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public icelake::implementation_simdjson_result_base { - 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 get_array() & noexcept; - simdjson_inline simdjson_result get_object() & noexcept; - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result get_value() noexcept; - simdjson_inline simdjson_result is_null() noexcept; - - template simdjson_inline simdjson_result get() & noexcept; - template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept; - - template simdjson_inline error_code get(T& out) & noexcept; - template simdjson_inline error_code get(T& out) && noexcept; -#if SIMDJSON_EXCEPTIONS - template ::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); - 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); - simdjson_inline operator icelake::ondemand::value() noexcept(false); -#endif - simdjson_inline simdjson_result count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) & noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(const char* key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](const char* key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(const char* key) & noexcept; - simdjson_inline simdjson_result type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_string() noexcept; - simdjson_inline simdjson_result current_location() noexcept; - simdjson_inline int32_t current_depth() const noexcept; - simdjson_inline bool at_end() const noexcept; - simdjson_inline bool is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - /** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */ - simdjson_inline simdjson_result raw_json_token() noexcept; - - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - }; - - -} // namespace simdjson - - - -namespace simdjson { - - template<> - struct simdjson_result : public icelake::implementation_simdjson_result_base { - 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 get_array() & noexcept; - simdjson_inline simdjson_result get_object() & noexcept; - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result get_value() noexcept; - simdjson_inline simdjson_result is_null() noexcept; -#if SIMDJSON_EXCEPTIONS - template ::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); - 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); - simdjson_inline operator icelake::ondemand::value() noexcept(false); -#endif - simdjson_inline simdjson_result count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) & noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(const char* key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](const char* key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(const char* key) & noexcept; - simdjson_inline simdjson_result type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_string() noexcept; - simdjson_inline simdjson_result current_location() noexcept; - simdjson_inline simdjson_result current_depth() const noexcept; - simdjson_inline simdjson_result is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - /** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */ - simdjson_inline simdjson_result raw_json_token() noexcept; - - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - }; - - -} // 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 -#include -#include -#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. - * - * ```c++ - * 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(); - * - */ - inline size_t truncated_bytes() const noexcept; - - class iterator { - public: - using value_type = simdjson_result; - using reference = simdjson_result; - using pointer = void; - 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 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. - * - * 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; - - 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; - }; - - /** - * 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) - */ - simdjson_inline document_stream( - ondemand::parser& parser, - const uint8_t* buf, - size_t len, - size_t batch_size, - bool allow_comma_separated - ) 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. */ - inline void next_document() 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; - /** - * 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 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. - */ - 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; - friend struct simdjson::internal::simdjson_result_base; - }; // document_stream - - } // namespace ondemand - } // namespace icelake -} // namespace simdjson - -namespace simdjson { - template<> - struct simdjson_result : public icelake::implementation_simdjson_result_base { - 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 so you can use C++ algorithms that rely on pairs. - */ - class field : public std::pair { - 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 unescaped_key(bool allow_replacement = false) 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. 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 - 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; - /** - * 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 start(value_iterator& parent_iter) noexcept; - static simdjson_inline simdjson_result start(const value_iterator& parent_iter, raw_json_string key) noexcept; - friend struct simdjson_result; - friend class object_iterator; - }; - - } // namespace ondemand - } // namespace icelake -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public icelake::implementation_simdjson_result_base { - 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 unescaped_key(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code unescaped_key(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result key() noexcept; - simdjson_inline simdjson_result key_raw_json_token() noexcept; - simdjson_inline simdjson_result escaped_key() noexcept; - simdjson_inline simdjson_result value() noexcept; - }; - -} // namespace simdjson - -#endif // SIMDJSON_GENERIC_ONDEMAND_FIELD_H -/* end file simdjson/generic/ondemand/field.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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -namespace simdjson { - namespace icelake { - namespace ondemand { - - /** - * 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; - - simdjson_inline simdjson_result begin() noexcept; - simdjson_inline simdjson_result end() noexcept; - /** - * Look up a field by name on an object (order-sensitive). - * - * 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 }`: - * - * ```c++ - * 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 }`. - * - * 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 find_field(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result 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. - * - * It is the default, however, 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 find_field_unordered(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) && 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("/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 at_pointer(std::string_view json_pointer) noexcept; - - /** - * Get the value associated with the given JSONPath expression. We only support - * JSONPath queries that trivially convertible to JSON Pointer queries: key - * names and array indices. - * - * @return The value associated with the given JSONPath expression, or: - * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - */ - inline simdjson_result at_path(std::string_view json_path) noexcept; - - /** - * Reset the iterator so that we are pointing back at the - * beginning of the object. You should still consume values only once even if you - * 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 reset() & 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 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 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 raw_json() noexcept; - - protected: - /** - * Go to the end of the object, no matter where you are right now. - */ - simdjson_inline error_code consume() noexcept; - static simdjson_inline simdjson_result start(value_iterator& iter) noexcept; - static simdjson_inline simdjson_result start_root(value_iterator& iter) noexcept; - static simdjson_inline simdjson_result 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{}; - - friend class value; - friend class document; - friend struct simdjson_result; - }; - - } // namespace ondemand - } // namespace icelake -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public icelake::implementation_simdjson_result_base { - 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 begin() noexcept; - simdjson_inline simdjson_result end() noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) && noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - - inline simdjson_result reset() noexcept; - inline simdjson_result is_empty() noexcept; - inline simdjson_result count_fields() & noexcept; - inline simdjson_result raw_json() noexcept; - - }; - -} // 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; - - // - // Iterator interface - // - - // Reads key and value, yielding them to the user. - // MUST ONLY BE CALLED ONCE PER ITERATION. - simdjson_inline simdjson_result 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 ',' - simdjson_inline object_iterator& operator++() noexcept; - - private: - /** - * 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; - friend class object; - }; - - } // namespace ondemand - } // namespace icelake -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public icelake::implementation_simdjson_result_base { - 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 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&) const noexcept; - // Assumes it's being compared with the end. true if depth >= iter->depth. - simdjson_inline bool operator!=(const simdjson_result&) const noexcept; - // Checks for ']' and ',' - simdjson_inline simdjson_result& 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/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 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 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 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 to_json_string(icelake::ondemand::array& x) noexcept; - inline simdjson_result to_json_string(simdjson_result x); - inline simdjson_result to_json_string(simdjson_result x); - inline simdjson_result to_json_string(simdjson_result x); - inline simdjson_result to_json_string(simdjson_result x); -} // namespace simdjson +struct has_custom_serialization : std::false_type {}; +#endif // SIMDJSON_SUPPORTS_CONCEPTS +namespace icelake { +namespace builder { /** - * 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 + * 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. */ -namespace simdjson { - namespace icelake { - namespace ondemand { +class string_builder { +public: + simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY); - /** - * 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); + 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::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 + 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 + simdjson_inline void append_key_value(key_type key, value_type value) noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + template + simdjson_inline void append_key_value(value_type value) noexcept; + + // Support for optional types (std::optional, etc.) + template + requires(!require_custom_serialization) + simdjson_inline void append(const T &opt); + + template + requires(require_custom_serialization) + simdjson_inline void append(T &&val); + + // Support for string-like types + template + requires(std::is_convertible::value || + std::is_same::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 +requires (!std::is_convertible::value && !require_custom_serialization) + 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; #if SIMDJSON_EXCEPTIONS - inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result 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 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&& 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&& 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 x); -#endif - } - } -} // namespace simdjson::icelake::ondemand - -#endif // SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H -/* end file simdjson/generic/ondemand/serialization.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/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::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::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::started(value_iterator& iter) noexcept { - bool has_value; - SIMDJSON_TRY(iter.started_array().get(has_value)); - return array(iter); - } - - simdjson_inline simdjson_result array::begin() noexcept { -#if SIMDJSON_DEVELOPMENT_CHECKS - if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } -#endif - return array_iterator(iter); - } - simdjson_inline simdjson_result array::end() noexcept { - return array_iterator(iter); - } - 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 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(starting_point), size_t(final_point - starting_point)); - } - - SIMDJSON_PUSH_DISABLE_WARNINGS - SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING - simdjson_inline simdjson_result array::count_elements() & 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 array after counting the number of elements. - iter.reset_array(); - return count; - } - SIMDJSON_POP_DISABLE_WARNINGS - - simdjson_inline simdjson_result array::is_empty() & noexcept { - bool is_not_empty; - auto error = iter.reset_array().get(is_not_empty); - if (error) { return error; } - return !is_not_empty; - } - - inline simdjson_result array::reset() & noexcept { - return iter.reset_array(); - } - - inline simdjson_result array::at_pointer(std::string_view json_pointer) noexcept { - if (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; } - - // Read the array index - size_t array_index = 0; - size_t i; - for (i = 0; i < json_pointer.length() && json_pointer[i] != '/'; i++) { - uint8_t digit = uint8_t(json_pointer[i] - '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; } - array_index = array_index * 10 + digit; - } - - // 0 followed by other digits is invalid - if (i > 1 && json_pointer[0] == '0') { return INVALID_JSON_POINTER; } // "JSON pointer array index has other characters after 0" - - // Empty string is invalid; so is a "/" with no digits before it - if (i == 0) { return INVALID_JSON_POINTER; } // "Empty string in JSON pointer array index" - // 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 std::string json_path_to_pointer_conversion(std::string_view json_path) { - if (json_path.empty() || (json_path.front() != '.' && - json_path.front() != '[')) { - return "-1"; // This is just a sentinel value, the caller should check for this and return an error. - } - - std::string result; - // Reserve space to reduce allocations, adjusting for potential increases due - // to escaping. - result.reserve(json_path.size() * 2); - - size_t i = 0; - - while (i < json_path.length()) { - if (json_path[i] == '.') { - result += '/'; - } - else if (json_path[i] == '[') { - result += '/'; - ++i; // Move past the '[' - while (i < json_path.length() && json_path[i] != ']') { - if (json_path[i] == '~') { - result += "~0"; - } - else if (json_path[i] == '/') { - result += "~1"; - } - else { - result += json_path[i]; - } - ++i; - } - if (i == json_path.length() || json_path[i] != ']') { - return "-1"; // Using sentinel value that will be handled as an error by the caller. - } - } - else { - if (json_path[i] == '~') { - result += "~0"; - } - else if (json_path[i] == '/') { - result += "~1"; - } - else { - result += json_path[i]; - } - } - ++i; - } - - return result; - } - - inline simdjson_result array::at_path(std::string_view json_path) noexcept { - auto json_pointer = json_path_to_pointer_conversion(json_path); - if (json_pointer == "-1") { return INVALID_JSON_POINTER; } - return at_pointer(json_pointer); - } - - simdjson_inline simdjson_result array::at(size_t index) noexcept { - size_t i = 0; - for (auto value : *this) { - if (i == index) { return value; } - i++; - } - return INDEX_OUT_OF_BOUNDS; - } - - } // namespace ondemand - } // namespace icelake -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result( - icelake::ondemand::array&& value - ) noexcept - : implementation_simdjson_result_base( - std::forward(value) - ) - { - } - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept - : implementation_simdjson_result_base(error) - { - } - - simdjson_inline simdjson_result simdjson_result::begin() noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() noexcept { - if (error()) { return error(); } - return first.end(); - } - simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); - } - simdjson_inline simdjson_result simdjson_result::is_empty() & noexcept { - if (error()) { return error(); } - return first.is_empty(); - } - simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } - simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { return error(); } - return first.at_path(json_path); - } - simdjson_inline simdjson_result simdjson_result::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 } - {} - - simdjson_inline simdjson_result array_iterator::operator*() noexcept { - 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 { - 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; - } - - } // namespace ondemand - } // namespace icelake -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result( - icelake::ondemand::array_iterator&& value - ) noexcept - : icelake::implementation_simdjson_result_base(std::forward(value)) - { - first.iter.assert_is_valid(); - } - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : icelake::implementation_simdjson_result_base({}, error) - { - } - - simdjson_inline simdjson_result simdjson_result::operator*() noexcept { - if (error()) { return error(); } - return *first; - } - simdjson_inline bool simdjson_result::operator==(const simdjson_result& other) const noexcept { - if (!first.iter.is_valid()) { return !error(); } - return first == other.first; - } - simdjson_inline bool simdjson_result::operator!=(const simdjson_result& other) const noexcept { - if (!first.iter.is_valid()) { return error(); } - return first != other.first; - } - simdjson_inline simdjson_result& simdjson_result::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_ARRAY_ITERATOR_INL_H -/* end file simdjson/generic/ondemand/array_iterator-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/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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -namespace simdjson { - namespace icelake { - namespace ondemand { - - simdjson_inline document::document(ondemand::json_iterator&& _iter) noexcept - : iter{ std::forward(_iter) } - { - logger::log_start_value(iter, "document"); - } - - simdjson_inline document document::start(json_iterator&& iter) noexcept { - return document(std::forward(iter)); - } - - inline void document::rewind() noexcept { - iter.rewind(); - } - - inline std::string document::to_debug_string() noexcept { - return iter.to_string(); - } - - inline simdjson_result 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 document::start_or_resume_object() noexcept { - if (iter.at_root()) { - return get_object(); - } - else { - return object::resume(resume_value_iterator()); - } - } - simdjson_inline simdjson_result document::get_value() noexcept { - // Make sure we start any arrays or objects before returning, so that start_root_() - // 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 document::get_array() & noexcept { - auto value = get_root_value_iterator(); - return array::start_root(value); - } - simdjson_inline simdjson_result 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 document::get_uint64() noexcept { - return get_root_value_iterator().get_root_uint64(true); - } - simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { - return get_root_value_iterator().get_root_uint64_in_string(true); - } - simdjson_inline simdjson_result document::get_int64() noexcept { - return get_root_value_iterator().get_root_int64(true); - } - simdjson_inline simdjson_result document::get_int64_in_string() noexcept { - return get_root_value_iterator().get_root_int64_in_string(true); - } - simdjson_inline simdjson_result document::get_double() noexcept { - return get_root_value_iterator().get_root_double(true); - } - simdjson_inline simdjson_result document::get_double_in_string() noexcept { - return get_root_value_iterator().get_root_double_in_string(true); - } - simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { - return get_root_value_iterator().get_root_string(true, allow_replacement); - } - template - 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 document::get_wobbly_string() noexcept { - return get_root_value_iterator().get_root_wobbly_string(true); - } - simdjson_inline simdjson_result document::get_raw_json_string() noexcept { - return get_root_value_iterator().get_root_raw_json_string(true); - } - simdjson_inline simdjson_result document::get_bool() noexcept { - return get_root_value_iterator().get_root_bool(true); - } - simdjson_inline simdjson_result document::is_null() noexcept { - return get_root_value_iterator().is_root_null(true); - } - - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } - - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_value(); } - - template simdjson_inline error_code document::get(T& out) & noexcept { - return get().get(out); - } - template simdjson_deprecated simdjson_inline error_code document::get(T& out) && noexcept { - return std::forward(*this).get().get(out); - } - -#if SIMDJSON_EXCEPTIONS - template - simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } - template - simdjson_inline document::operator T() & noexcept(false) { return get(); } - 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) { return get_string(false); } - simdjson_inline document::operator raw_json_string() noexcept(false) { 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 document::count_elements() & noexcept { - auto a = get_array(); - simdjson_result 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 document::count_fields() & noexcept { - auto a = get_object(); - simdjson_result 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 document::at(size_t index) & noexcept { - auto a = get_array(); - return a.at(index); - } - simdjson_inline simdjson_result document::begin() & noexcept { - return get_array().begin(); - } - simdjson_inline simdjson_result document::end() & noexcept { - return {}; - } - - simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { - return start_or_resume_object().find_field(key); - } - simdjson_inline simdjson_result document::find_field(const char* key) & noexcept { - return start_or_resume_object().find_field(key); - } - simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { - return start_or_resume_object().find_field_unordered(key); - } - simdjson_inline simdjson_result document::find_field_unordered(const char* key) & noexcept { - return start_or_resume_object().find_field_unordered(key); - } - simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { - return start_or_resume_object()[key]; - } - simdjson_inline simdjson_result document::operator[](const char* key) & noexcept { - return start_or_resume_object()[key]; - } - - simdjson_inline error_code document::consume() noexcept { - auto error = iter.skip_child(0); - if (error) { iter.abandon(); } - return error; - } - - simdjson_inline simdjson_result 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(starting_point), size_t(final_point - starting_point)); - } - - simdjson_inline simdjson_result document::type() noexcept { - return get_root_value_iterator().type(); - } - - simdjson_inline simdjson_result document::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 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 document::is_integer() noexcept { - return get_root_value_iterator().is_root_integer(true); - } - - simdjson_inline simdjson_result document::get_number_type() noexcept { - return get_root_value_iterator().get_root_number_type(true); - } - - simdjson_inline simdjson_result document::get_number() noexcept { - return get_root_value_iterator().get_root_number(true); - } - - - simdjson_inline simdjson_result document::raw_json_token() noexcept { - auto _iter = get_root_value_iterator(); - return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); - } - - simdjson_inline simdjson_result 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 document::at_path(std::string_view json_path) noexcept { - rewind(); // Rewind the document each time at_pointer is called - if (json_path.empty()) { - return this->get_value(); - } - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) { - case json_type::array: - return (*this).get_array().at_path(json_path); - case json_type::object: - return (*this).get_object().at_path(json_path); - default: - return INVALID_JSON_POINTER; - } - } - - } // namespace ondemand - } // namespace icelake -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result( - icelake::ondemand::document&& value - ) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) - { - } - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept : - implementation_simdjson_result_base( - error - ) - { - } - simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); - } - simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); - } - simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; - } - simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() & noexcept { - return {}; - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char* key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](const char* key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::find_field(const char* key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { - if (error()) { return error(); } - return first.get_array(); - } - simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { - if (error()) { return error(); } - return first.get_object(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); - } - simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); - } - simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); - } - template - simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(receiver, allow_replacement); - } - simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); - } - simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); - } - simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); - } - simdjson_inline simdjson_result simdjson_result::get_value() noexcept { - if (error()) { return error(); } - return first.get_value(); - } - simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); - } - - template - simdjson_inline simdjson_result simdjson_result::get() & noexcept { - if (error()) { return error(); } - return first.get(); - } - template - simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { - if (error()) { return error(); } - return std::forward(first).get(); - } - template - simdjson_inline error_code simdjson_result::get(T& out) & noexcept { - if (error()) { return error(); } - return first.get(out); - } - template - simdjson_inline error_code simdjson_result::get(T& out) && noexcept { - if (error()) { return error(); } - return std::forward(first).get(out); - } - - template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; - template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { - if (error()) { return error(); } - return std::forward(first); - } - template<> simdjson_inline error_code simdjson_result::get(icelake::ondemand::document& out) & noexcept = delete; - template<> simdjson_inline error_code simdjson_result::get(icelake::ondemand::document& out) && noexcept { - if (error()) { return error(); } - out = std::forward(first); - return SUCCESS; - } - - simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); - } - - simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); - } - - simdjson_inline simdjson_result simdjson_result::is_string() noexcept { - if (error()) { return error(); } - return first.is_string(); - } - - simdjson_inline bool simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); - } - - simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); - } - - simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); - } - - simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); - } - - -#if SIMDJSON_EXCEPTIONS - template ::value == false>::type> - simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator icelake::ondemand::array() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator icelake::ondemand::object() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator icelake::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator icelake::ondemand::value() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } + /** + * 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 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 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; + +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); + + /** + * 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. + */ + simdjson_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 buffer{}; + size_t position{0}; + size_t capacity{0}; + bool is_valid{true}; +}; - simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); - } - simdjson_inline bool simdjson_result::at_end() const noexcept { - if (error()) { return error(); } - return first.at_end(); - } - - - simdjson_inline int32_t simdjson_result::current_depth() const noexcept { - if (error()) { return error(); } - return first.current_depth(); - } - - simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); - } - - simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } - - simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { return error(); } - return first.at_path(json_path); - } - -} // namespace simdjson - - -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 document_reference::get_array() & noexcept { return doc->get_array(); } - simdjson_inline simdjson_result 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 document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } - simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } - simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } - simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } - simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } - simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } - simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } - template - 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 document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } - simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } - simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } - simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } - simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } -#if SIMDJSON_EXCEPTIONS - template - simdjson_inline document_reference::operator T() noexcept(false) { return get(); } - 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 document_reference::count_elements() & noexcept { return doc->count_elements(); } - simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } - simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } - simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } - simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } - simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } - simdjson_inline simdjson_result document_reference::find_field(const char* key) & noexcept { return doc->find_field(key); } - simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } - simdjson_inline simdjson_result document_reference::operator[](const char* key) & noexcept { return (*doc)[key]; } - simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } - simdjson_inline simdjson_result document_reference::find_field_unordered(const char* key) & noexcept { return doc->find_field_unordered(key); } - simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } - simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } - simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } - simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } - simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } - simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } - simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } - simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } - simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } - simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } - simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } - simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } - simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json(); } - simdjson_inline document_reference::operator document& () const noexcept { return *doc; } - - } // namespace ondemand - } // namespace icelake -} // namespace simdjson - - - -namespace simdjson { - simdjson_inline simdjson_result::simdjson_result(icelake::ondemand::document_reference value, error_code error) - noexcept : implementation_simdjson_result_base(std::forward(value), error) {} - - - simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); - } - simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); - } - simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; - } - simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() & noexcept { - return {}; - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char* key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](const char* key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::find_field(const char* key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { - if (error()) { return error(); } - return first.get_array(); - } - simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { - if (error()) { return error(); } - return first.get_object(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); - } - simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); - } - simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); - } - template - simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(receiver, allow_replacement); - } - simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); - } - simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); - } - simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); - } - simdjson_inline simdjson_result simdjson_result::get_value() noexcept { - if (error()) { return error(); } - return first.get_value(); - } - simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); - } - simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); - } - simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); - } - simdjson_inline simdjson_result simdjson_result::is_string() noexcept { - if (error()) { return error(); } - return first.is_string(); - } - simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); - } - simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); - } - simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); - } - simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); - } -#if SIMDJSON_EXCEPTIONS - template ::value == false>::type> - simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator icelake::ondemand::array() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator icelake::ondemand::object() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator icelake::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator icelake::ondemand::value() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } -#endif - - simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); - } - - simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); - } - - simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } - - simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { - return error(); - } - return first.at_path(json_path); - } - -} // namespace simdjson - -#endif // SIMDJSON_GENERIC_ONDEMAND_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 -#include - -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 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 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 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 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 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 - ) 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 }, - 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 }, - allow_comma_separated{ false }, - 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 { - 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 document_stream::iterator::operator*() noexcept { - //if(stream->error) { return stream->error; } - return simdjson_result(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::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; } - doc_index = batch_start; - 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 - 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(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; - } - } - } - - inline void document_stream::next_document() noexcept { - // 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) { doc.iter.consume_character(','); } - // 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; - if (remaining <= batch_size) { - return p.implementation->stage1(&buf[_batch_start], remaining, stage1_mode::streaming_final); - } - else { - return p.implementation->stage1(&buf[_batch_start], batch_size, stage1_mode::streaming_partial); - } - } - - 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 { - auto depth = stream->doc.iter.depth(); - auto cur_struct_index = stream->doc.iter._root - stream->parser->implementation->structural_indexes.get(); - - // If at root, process the first token to determine if scalar value - if (stream->doc.iter.at_root()) { - 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 - // TODO: We could remove trailing whitespaces - // This returns a string spanning from start of value to the beginning of the next document (excluded) - { - auto next_index = 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(stream->buf) + current_index(); - while (svlen > 1 && (std::isspace(start[svlen - 1]) || start[svlen - 1] == '\0')) { - svlen--; - } - return std::string_view(start, svlen); - } - } - cur_struct_index++; - } - - while (cur_struct_index <= static_cast(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(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::simdjson_result( - error_code error - ) noexcept : - implementation_simdjson_result_base(error) - { - } - simdjson_inline simdjson_result::simdjson_result( - icelake::ondemand::document_stream&& value - ) noexcept : - implementation_simdjson_result_base( - std::forward(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 + +#if !SIMDJSON_STATIC_REFLECTION +// fallback implementation until we have static reflection +template +simdjson_warn_unused simdjson_result 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 +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_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): #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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ +#if SIMDJSON_STATIC_REFLECTION -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() {} - - simdjson_inline field::field(raw_json_string key, ondemand::value&& value) noexcept - : std::pair(key, std::forward(value)) - { - } - - simdjson_inline simdjson_result 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::start(const value_iterator& parent_iter, raw_json_string key) noexcept { - return field(key, parent_iter.child()); - } - - simdjson_inline simdjson_warn_unused simdjson_result 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 answer = first.unescape(second.iter.json_iter(), allow_replacement); - first.consume(); - return answer; - } - - template - 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)); - 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(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(first.buf), end_quote - first.buf); - } - - simdjson_inline value& field::value() & noexcept { - return second; - } - - simdjson_inline value field::value() && noexcept { - return std::forward(*this).second; - } - - } // namespace ondemand - } // namespace icelake -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result( - icelake::ondemand::field&& value - ) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) - { - } - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept : - implementation_simdjson_result_base(error) - { - } - - simdjson_inline simdjson_result simdjson_result::key() noexcept { - if (error()) { return error(); } - return first.key(); - } - - simdjson_inline simdjson_result simdjson_result::key_raw_json_token() noexcept { - if (error()) { return error(); } - return first.key_raw_json_token(); - } - - simdjson_inline simdjson_result simdjson_result::escaped_key() noexcept { - if (error()) { return error(); } - return first.escaped_key(); - } - - simdjson_inline simdjson_result simdjson_result::unescaped_key(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.unescaped_key(allow_replacement); - } - - template - simdjson_inline error_code simdjson_result::unescaped_key(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.unescaped_key(receiver, allow_replacement); - } - - simdjson_inline simdjson_result simdjson_result::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(other.token)), - parser{ other.parser }, - _string_buf_loc{ other._string_buf_loc }, - error{ other.error }, - _depth{ other._depth }, - _root{ other._root }, - _streaming{ other._streaming } - { - 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; - 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 } - - { - 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 '"': - if (*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(TAPE_ERROR, "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; - } - - 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 { -#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(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 json_iterator::current_location() const noexcept { - if (!is_alive()) { // Unrecoverable error - if (!at_root()) { - return reinterpret_cast(token.peek(-1)); - } - else { - return reinterpret_cast(token.peek()); - } - } - if (at_end()) { - return OUT_OF_BOUNDS; - } - return reinterpret_cast(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_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 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); - SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc)); - return result; -#else - return parser->unescape(in, _string_buf_loc, allow_replacement); -#endif - } - - simdjson_inline simdjson_result json_iterator::unescape_wobbly(raw_json_string in) noexcept { -#if SIMDJSON_DEVELOPMENT_CHECKS - auto result = parser->unescape_wobbly(in, _string_buf_loc); - SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc)); - 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_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_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::simdjson_result(icelake::ondemand::json_iterator&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(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; - default: SIMDJSON_UNREACHABLE(); - } - return out; - } - -#if SIMDJSON_EXCEPTIONS - inline std::ostream& operator<<(std::ostream& out, simdjson_result& 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::simdjson_result(icelake::ondemand::json_type&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(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 +#include #include +#include +#include +#include +#include +#include +#include +// #include // for std::define_static_string - header not available yet namespace simdjson { - namespace icelake { - namespace ondemand { - namespace logger { +namespace icelake { +namespace builder { - 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. +template + requires(concepts::container_but_not_string && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + auto it = t.begin(); + auto end = t.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} - // Helper to turn unprintable or newline characters into spaces - static inline char printable_char(char c) { - if (c >= 0x20) { - return c; - } - else { - return ' '; - } - } +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +constexpr void atom(string_builder &b, const T &t) { + b.escape_and_append_with_quotes(t); +} - template - 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_s); - if (size <= 0) return std::string(); - std::unique_ptr 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 - 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)...); - } - - template - 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)...); - 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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -namespace simdjson { - namespace icelake { - namespace ondemand { - - simdjson_inline simdjson_result 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(key.size()), key.data()); - return NO_SUCH_FIELD; - } - return value(iter.child()); - } - simdjson_inline simdjson_result 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(key.size()), key.data()); - return NO_SUCH_FIELD; - } - return value(iter.child()); - } - simdjson_inline simdjson_result object::operator[](const std::string_view key) & noexcept { - return find_field_unordered(key); - } - simdjson_inline simdjson_result object::operator[](const std::string_view key) && noexcept { - return std::forward(*this).find_field_unordered(key); - } - simdjson_inline simdjson_result object::find_field(const std::string_view key) & noexcept { - 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(key.size()), key.data()); - return NO_SUCH_FIELD; - } - return value(iter.child()); - } - simdjson_inline simdjson_result object::find_field(const std::string_view key) && noexcept { - 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(key.size()), key.data()); - return NO_SUCH_FIELD; - } - return value(iter.child()); - } - - simdjson_inline simdjson_result object::start(value_iterator& iter) noexcept { - SIMDJSON_TRY(iter.start_object().error()); - return object(iter); - } - simdjson_inline simdjson_result object::start_root(value_iterator& iter) noexcept { - SIMDJSON_TRY(iter.start_root_object().error()); - return object(iter); - } - 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 object::raw_json() noexcept { - 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(starting_point), size_t(final_point - starting_point)); - } - - simdjson_inline simdjson_result 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::begin() noexcept { -#if SIMDJSON_DEVELOPMENT_CHECKS - if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } -#endif - return object_iterator(iter); - } - simdjson_inline simdjson_result object::end() noexcept { - return object_iterator(iter); - } - - inline simdjson_result object::at_pointer(std::string_view json_pointer) noexcept { - if (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 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 object::at_path(std::string_view json_path) noexcept { - auto json_pointer = json_path_to_pointer_conversion(json_path); - if (json_pointer == "-1") { - return INVALID_JSON_POINTER; - } - return at_pointer(json_pointer); - } - - simdjson_inline simdjson_result object::count_fields() & noexcept { - size_t count{ 0 }; - // Important: we do not consume any of the values. - 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 object::is_empty() & noexcept { - 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 object::reset() & noexcept { - return iter.reset_object(); - } - - } // namespace ondemand - } // namespace icelake -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result(icelake::ondemand::object&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} - - simdjson_inline simdjson_result simdjson_result::begin() noexcept { - if (error()) { return error(); } - return first.begin(); +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &m) { + if (m.empty()) { + b.append_raw("{}"); + return; + } + b.append('{'); + bool first = true; + for (const auto& [key, value] : m) { + if (!first) { + b.append(','); } - simdjson_inline simdjson_result simdjson_result::end() noexcept { - if (error()) { return error(); } - return first.end(); + first = false; + // Keys must be convertible to string_view per the concept + b.escape_and_append_with_quotes(key); + b.append(':'); + atom(b, value); + } + b.append('}'); +} + + +template::value && !std::is_same_v>::type> +constexpr void atom(string_builder &b, const number_type t) { + b.append(t); +} + +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, t.[:dm:]); + i++; + }; + b.append('}'); +} + +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &opt) { + if (opt) { + atom(b, opt.value()); + } else { + b.append_raw("null"); + } +} + +// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &ptr) { + if (ptr) { + atom(b, *ptr); + } else { + b.append_raw("null"); + } +} + +// Support for enums - serialize as string representation using expand approach from P2996R12 +template + requires(std::is_enum_v && !require_custom_serialization) +void atom(string_builder &b, const T &e) { +#if SIMDJSON_STATIC_REFLECTION + 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(std::meta::identifier_of(enum_val))); + if (e == [:enum_val:]) { + b.append_raw(enum_str); + return; } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) && noexcept { - if (error()) { return error(); } - return std::forward(first).find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) && noexcept { - if (error()) { return error(); } - return std::forward(first)[key]; - } - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) && noexcept { - if (error()) { return error(); } - return std::forward(first).find_field(key); - } - - simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } - - simdjson_inline simdjson_result simdjson_result::at_path( - std::string_view json_path) noexcept { - if (error()) { - return error(); - } - return first.at_path(json_path); - } - - inline simdjson_result simdjson_result::reset() noexcept { - if (error()) { return error(); } - return first.reset(); - } - - inline simdjson_result simdjson_result::is_empty() noexcept { - if (error()) { return error(); } - return first.is_empty(); - } - - simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); - } - - simdjson_inline simdjson_result simdjson_result::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 } - {} - - simdjson_inline simdjson_result object_iterator::operator*() noexcept { - 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 { - // 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::simdjson_result( - icelake::ondemand::object_iterator&& value - ) noexcept - : implementation_simdjson_result_base(std::forward(value)) - { - first.iter.assert_is_valid(); - } - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base({}, error) - { - } - - simdjson_inline simdjson_result simdjson_result::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::operator==(const simdjson_result& 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::operator!=(const simdjson_result& other) const noexcept { - if (!first.iter.is_valid()) { return error(); } - return first != other.first; - } - // Checks for ']' and ',' - simdjson_inline simdjson_result& simdjson_result::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/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; - 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 parser::iterate(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } - - json.remove_utf8_bom(); - - // 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(json.data()), json.length(), stage1_mode::regular)); - return document::start({ reinterpret_cast(json.data()), this }); - } - -#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON - simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } - - json.remove_utf8_bom(); - - // 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(json.data()), json.length(), stage1_mode::regular); - if (err) { - if (err != UNCLOSED_STRING) - return err; - } - return document::start({ reinterpret_cast(json.data()), this, true }); - } -#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON - - simdjson_warn_unused simdjson_inline simdjson_result 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 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 parser::iterate(std::string_view json, size_t allocated) & noexcept { - return iterate(padded_string_view(json, allocated)); - } - - simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(std::string& json) & noexcept { - if (json.capacity() - json.size() < SIMDJSON_PADDING) { - json.reserve(json.size() + SIMDJSON_PADDING); - } - return iterate(padded_string_view(json)); - } - - simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const std::string& json) & noexcept { - return iterate(padded_string_view(json)); - } - - simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const simdjson_result& result) & noexcept { - // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 parser::iterate(const simdjson_result& result) & noexcept { - // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 parser::iterate_raw(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_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(json.data()), json.length(), stage1_mode::regular)); - return json_iterator(reinterpret_cast(json.data()), this); - } - - inline simdjson_result parser::iterate_many(const uint8_t* buf, size_t len, size_t batch_size, bool allow_comma_separated) 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 (allow_comma_separated && batch_size < len) { batch_size = len; } - return document_stream(*this, buf, len, batch_size, allow_comma_separated); - } - inline simdjson_result parser::iterate_many(const char* buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept { - return iterate_many(reinterpret_cast(buf), len, batch_size, allow_comma_separated); - } - inline simdjson_result parser::iterate_many(const std::string& s, size_t batch_size, bool allow_comma_separated) noexcept { - return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated); - } - inline simdjson_result parser::iterate_many(const padded_string& s, size_t batch_size, bool allow_comma_separated) noexcept { - return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated); - } - - 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 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(dst), end - dst); - dst = end; - return result; - } - - simdjson_inline simdjson_warn_unused simdjson_result 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(dst), end - dst); - dst = end; - return result; - } - - } // namespace ondemand - } // namespace icelake -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result(icelake::ondemand::parser&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(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(buf); } - - - simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept { - size_t pos{ 0 }; - // if the content has no escape character, just scan through it quickly! - for (; pos < target.size() && target[pos] != '\\'; pos++) {} - // slow path may begin. - bool escaping{ false }; - for (; pos < target.size(); pos++) { - if ((target[pos] == '"') && !escaping) { - return false; - } - else if (target[pos] == '\\') { - escaping = !escaping; - } - else { - escaping = false; - } - } - return true; - } - - simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* target) noexcept { - size_t pos{ 0 }; - // if the content has no escape character, just scan through it quickly! - for (; target[pos] && target[pos] != '\\'; pos++) {} - // slow path may begin. - bool escaping{ false }; - for (; target[pos]; pos++) { - if ((target[pos] == '"') && !escaping) { - return false; - } - else if (target[pos] == '\\') { - escaping = !escaping; - } - else { - escaping = false; - } - } - 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()); - } - const char* r{ raw() }; - size_t pos{ 0 }; - for (; pos < target.size(); pos++) { - if (r[pos] != target[pos]) { return false; } - } - if (r[pos] != '"') { return false; } - return true; - } - - 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 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 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::simdjson_result(icelake::ondemand::raw_json_string&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} - - simdjson_inline simdjson_result simdjson_result::raw() const noexcept { - if (error()) { return error(); } - return first.raw(); - } - simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::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 simdjson_result::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/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): #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 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 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 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 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 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 to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); - } - - inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); - } - - inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); - } - - inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); - } - - inline simdjson_result to_json_string(simdjson_result 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 x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); - } + }; + // Fallback to integer if enum value not found + atom(b, static_cast>(e)); #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); - } - } + // Fallback: serialize as integer if reflection not available + atom(b, static_cast>(e)); #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 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&& x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); - } - inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& 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 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 - } +// Support for appendable containers that don't have operator[] (sets, etc.) +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &container) { + if (container.empty()) { + b.append_raw("[]"); + return; + } + b.append('['); + bool first = true; + for (const auto& item : container) { + if (!first) { + b.append(','); } -} // namespace simdjson::icelake::ondemand + first = false; + atom(b, item); + } + b.append(']'); +} -#endif // SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H -/* end file simdjson/generic/ondemand/serialization-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 +// append functions that delegate to atom functions for primitive types +template + requires(std::is_arithmetic_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +// works for struct +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^Z, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, z.[:dm:]); + i++; + }; + b.append('}'); +} + +// works for container that have begin() and end() iterators +template + requires(concepts::container_but_not_string && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + auto it = z.begin(); + auto end = z.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} + +template + requires (require_custom_serialization) +void append(string_builder &b, const Z &z) { + b.append(z); +} + + +template +simdjson_warn_unused simdjson_result to_json_string(const Z &z, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +template +simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + 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 +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 + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +void extract_from(string_builder &b, const T &obj) { + // 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) || ...); + }; + + b.append('{'); + bool first = true; + + // 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_public(mem)) { + 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 (should_extract(key)) { + if (!first) { + b.append(','); + } + first = false; + + // Serialize the key + constexpr auto quoted_key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem))); + b.append_raw(quoted_key); + b.append(':'); + + // Serialize the value + atom(b, obj.[:mem:]); + } + } + }; + + b.append('}'); +} + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + extract_from(b, obj); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +} // namespace builder +} // namespace icelake +// Alias the function template to 'to' in the global namespace +template +simdjson_warn_unused simdjson_result to_json(const Z &z, size_t initial_capacity = icelake::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + icelake::builder::string_builder b(initial_capacity); + icelake::builder::append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} +template +simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = icelake::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + icelake::builder::string_builder b(initial_capacity); + icelake::builder::append(b, z); + std::string_view view; + if(auto e = b.view().get(view); e) { return e; } + s.assign(view); + return SUCCESS; +} +// Global namespace function for extract_from +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result 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(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): #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): #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 */ -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 +#if SIMDJSON_STATIC_REFLECTION namespace simdjson { +namespace icelake { +namespace builder { - simdjson_inline simdjson_result::simdjson_result(icelake::ondemand::token_iterator&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} +/** + * 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 +simdjson_warn_unused simdjson_result 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 + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result 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(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 +simdjson_warn_unused simdjson_result 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 + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result 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(obj, opts, initial_capacity); +} } // 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-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 +#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 +#include +#include +#ifndef SIMDJSON_GENERIC_STRING_BUILDER_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): #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 SIMDJSON_EXPERIMENTAL_HAS_NEON +#include +#ifdef _MSC_VER +#include +#endif +#endif +#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 +#include +#ifdef _MSC_VER +#include +#endif +#endif + namespace simdjson { - namespace icelake { - namespace ondemand { +namespace icelake { +namespace builder { - 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; - } +static SIMDJSON_CONSTEXPR_LAMBDA std::array + 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}; - simdjson_inline simdjson_result value::get_array() noexcept { - return array::start(iter); - } - simdjson_inline simdjson_result value::get_object() noexcept { - return object::start(iter); - } - simdjson_inline simdjson_result value::start_or_resume_object() noexcept { - if (iter.at_start()) { - return get_object(); - } - else { - return object::resume(iter); - } - } +/** - simdjson_inline simdjson_result value::get_raw_json_string() noexcept { - return iter.get_raw_json_string(); - } - simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { - return iter.get_string(allow_replacement); - } - template - 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 value::get_wobbly_string() noexcept { - return iter.get_wobbly_string(); - } - simdjson_inline simdjson_result value::get_double() noexcept { - return iter.get_double(); - } - simdjson_inline simdjson_result value::get_double_in_string() noexcept { - return iter.get_double_in_string(); - } - simdjson_inline simdjson_result value::get_uint64() noexcept { - return iter.get_uint64(); - } - simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { - return iter.get_uint64_in_string(); - } - simdjson_inline simdjson_result value::get_int64() noexcept { - return iter.get_int64(); - } - simdjson_inline simdjson_result value::get_int64_in_string() noexcept { - return iter.get_int64_in_string(); - } - simdjson_inline simdjson_result value::get_bool() noexcept { - return iter.get_bool(); - } - simdjson_inline simdjson_result value::is_null() noexcept { - return iter.is_null(); - } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } +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). - template simdjson_inline error_code value::get(T& out) noexcept { - return get().get(out); - } +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; +} + +**/ + +SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline bool +simple_needs_escaping(std::string_view v) { + for (char c : v) { + // a table lookup is faster than a series of comparisons + if (json_quotable_character[static_cast(c)]) { + return true; + } + } + return false; +} + +#if SIMDJSON_EXPERIMENTAL_HAS_NEON +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + uint8x16_t running = vdupq_n_u8(0); + uint8x16_t v34 = vdupq_n_u8(34); + uint8x16_t v92 = vdupq_n_u8(92); + + for (; i + 15 < view.size(); i += 16) { + uint8x16_t word = vld1q_u8((const uint8_t *)view.data() + i); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + if (i < view.size()) { + uint8x16_t word = + vld1q_u8((const uint8_t *)view.data() + view.length() - 16); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + return vmaxvq_u32(vreinterpretq_u32_u8(running)) != 0; +} +#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2 +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + __m128i running = _mm_setzero_si128(); + for (; i + 15 < view.size(); i += 16) { + + __m128i word = + _mm_loadu_si128(reinterpret_cast(view.data() + i)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + if (i < view.size()) { + __m128i word = _mm_loadu_si128( + reinterpret_cast(view.data() + view.length() - 16)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + return _mm_movemask_epi8(running) != 0; +} +#else +simdjson_inline bool fast_needs_escaping(std::string_view view) { + return simple_needs_escaping(view); +} +#endif + +// 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(*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(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)); + + if (vmaxvq_u32(vreinterpretq_u32_u8(needs_escape)) != 0) { + // Found quotable character - extract exact byte position using ctz + uint64x2_t as64 = vreinterpretq_u64_u8(needs_escape); + uint64_t lo = vgetq_lane_u64(as64, 0); + uint64_t hi = vgetq_lane_u64(as64, 1); + size_t offset = ptr - reinterpret_cast(view.data()); +#ifdef _MSC_VER + unsigned long trailing_zero = 0; + if (lo != 0) { + _BitScanForward64(&trailing_zero, lo); + return offset + trailing_zero / 8; + } else { + _BitScanForward64(&trailing_zero, hi); + return offset + 8 + trailing_zero / 8; + } +#else + if (lo != 0) { + return offset + __builtin_ctzll(lo) / 8; + } else { + return offset + 8 + __builtin_ctzll(hi) / 8; + } +#endif + } + 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(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(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(view.data()); +#ifdef _MSC_VER + unsigned long trailing_zero = 0; + _BitScanForward(&trailing_zero, mask); + return offset + trailing_zero; +#else + return offset + __builtin_ctz(mask); +#endif + } + 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(); + } +} + +// 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 out - initout; +} + +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; +} + +simdjson_inline void string_builder::grow_buffer(size_t desired_capacity) { + if (!is_valid) { + return; + } + std::unique_ptr 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 { + +template ::value>::type> +simdjson_really_inline int int_log2(number_type x) { + return 63 - leading_zeroes(uint64_t(x) | 1); +} + +simdjson_really_inline int fast_digit_count_32(uint32_t x) { + static uint64_t table[] = { + 4294967296, 8589934582, 8589934582, 8589934582, 12884901788, + 12884901788, 12884901788, 17179868184, 17179868184, 17179868184, + 21474826480, 21474826480, 21474826480, 21474826480, 25769703776, + 25769703776, 25769703776, 30063771072, 30063771072, 30063771072, + 34349738368, 34349738368, 34349738368, 34349738368, 38554705664, + 38554705664, 38554705664, 41949672960, 41949672960, 41949672960, + 42949672960, 42949672960}; + return uint32_t((x + table[int_log2(x)]) >> 32); +} + +simdjson_really_inline int fast_digit_count_64(uint64_t x) { + static uint64_t table[] = {9, + 99, + 999, + 9999, + 99999, + 999999, + 9999999, + 99999999, + 999999999, + 9999999999, + 99999999999, + 999999999999, + 9999999999999, + 99999999999999, + 999999999999999ULL, + 9999999999999999ULL, + 99999999999999999ULL, + 999999999999999999ULL, + 9999999999999999999ULL}; + int y = (19 * int_log2(x) >> 6); + y += x > table[y]; + return y + 1; +} + +template ::value>::type> +simdjson_really_inline size_t digit_count(number_type v) noexcept { + static_assert(sizeof(number_type) == 8 || sizeof(number_type) == 4 || + sizeof(number_type) == 2 || sizeof(number_type) == 1, + "We only support 8-bit, 16-bit, 32-bit and 64-bit numbers"); + SIMDJSON_IF_CONSTEXPR(sizeof(number_type) <= 4) { + return fast_digit_count_32(static_cast(v)); + } + else { + return fast_digit_count_64(static_cast(v)); + } +} +static const char decimal_table[200] = { + 0x30, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35, + 0x30, 0x36, 0x30, 0x37, 0x30, 0x38, 0x30, 0x39, 0x31, 0x30, 0x31, 0x31, + 0x31, 0x32, 0x31, 0x33, 0x31, 0x34, 0x31, 0x35, 0x31, 0x36, 0x31, 0x37, + 0x31, 0x38, 0x31, 0x39, 0x32, 0x30, 0x32, 0x31, 0x32, 0x32, 0x32, 0x33, + 0x32, 0x34, 0x32, 0x35, 0x32, 0x36, 0x32, 0x37, 0x32, 0x38, 0x32, 0x39, + 0x33, 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x33, 0x34, 0x33, 0x35, + 0x33, 0x36, 0x33, 0x37, 0x33, 0x38, 0x33, 0x39, 0x34, 0x30, 0x34, 0x31, + 0x34, 0x32, 0x34, 0x33, 0x34, 0x34, 0x34, 0x35, 0x34, 0x36, 0x34, 0x37, + 0x34, 0x38, 0x34, 0x39, 0x35, 0x30, 0x35, 0x31, 0x35, 0x32, 0x35, 0x33, + 0x35, 0x34, 0x35, 0x35, 0x35, 0x36, 0x35, 0x37, 0x35, 0x38, 0x35, 0x39, + 0x36, 0x30, 0x36, 0x31, 0x36, 0x32, 0x36, 0x33, 0x36, 0x34, 0x36, 0x35, + 0x36, 0x36, 0x36, 0x37, 0x36, 0x38, 0x36, 0x39, 0x37, 0x30, 0x37, 0x31, + 0x37, 0x32, 0x37, 0x33, 0x37, 0x34, 0x37, 0x35, 0x37, 0x36, 0x37, 0x37, + 0x37, 0x38, 0x37, 0x39, 0x38, 0x30, 0x38, 0x31, 0x38, 0x32, 0x38, 0x33, + 0x38, 0x34, 0x38, 0x35, 0x38, 0x36, 0x38, 0x37, 0x38, 0x38, 0x38, 0x39, + 0x39, 0x30, 0x39, 0x31, 0x39, 0x32, 0x39, 0x33, 0x39, 0x34, 0x39, 0x35, + 0x39, 0x36, 0x39, 0x37, 0x39, 0x38, 0x39, 0x39, +}; +} // namespace internal + +template +simdjson_inline void string_builder::append(number_type v) noexcept { + static_assert(std::is_same::value || + std::is_integral::value || + std::is_floating_point::value, + "Unsupported number type"); + // If C++17 is available, we can 'if constexpr' here. + SIMDJSON_IF_CONSTEXPR(std::is_same::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::value) { + // Process 4 digits at a time instead of 2, reducing store operations + // and divisions by approximately half for large numbers. + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + unsigned_type pv = static_cast(v); + size_t dc = internal::digit_count(pv); + char *write_pointer = buffer.get() + position + dc - 1; + + // Process 4 digits per iteration for large numbers + while (pv >= 10000) { + unsigned_type q = pv / 10000; + unsigned_type r = pv % 10000; + unsigned_type r_hi = r / 100; // High 2 digits of remainder + unsigned_type r_lo = r % 100; // Low 2 digits of remainder + // Write low 2 digits first (rightmost), then high 2 digits + memcpy(write_pointer - 1, &internal::decimal_table[r_lo * 2], 2); + memcpy(write_pointer - 3, &internal::decimal_table[r_hi * 2], 2); + write_pointer -= 4; + pv = q; + } + + // Handle remaining 1-4 digits with original 2-digit loop + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_integral::value) { + // Same 4-digit batching as unsigned path for signed integers + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + bool negative = v < 0; + unsigned_type pv = static_cast(v); + if (negative) { + pv = 0 - pv; // the 0 is for Microsoft + } + size_t dc = internal::digit_count(pv); + // by always writing the minus sign, we avoid the branch. + buffer.get()[position] = '-'; + position += negative ? 1 : 0; + char *write_pointer = buffer.get() + position + dc - 1; + + // Process 4 digits per iteration for large numbers + while (pv >= 10000) { + unsigned_type q = pv / 10000; + unsigned_type r = pv % 10000; + unsigned_type r_hi = r / 100; + unsigned_type r_lo = r % 100; + memcpy(write_pointer - 1, &internal::decimal_table[r_lo * 2], 2); + memcpy(write_pointer - 3, &internal::decimal_table[r_hi * 2], 2); + write_pointer -= 4; + pv = q; + } + + // Handle remaining 1-4 digits + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_floating_point::value) { + constexpr size_t max_number_size = 24; + if (capacity_check(max_number_size)) { + // 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. + 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. + 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 +simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept { + escape_and_append_with_quotes(constevalutil::string_constant::value); +} +#endif + +simdjson_inline void string_builder::append_raw(const char *c) noexcept { + size_t len = std::strlen(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; + } +} +#if SIMDJSON_SUPPORTS_CONCEPTS +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +simdjson_inline void string_builder::append(const T &opt) { + if (opt) { + append(*opt); + } else { + append_null(); + } +} + +template + requires(require_custom_serialization) +simdjson_inline void string_builder::append(T &&val) { + serialize(*this, std::forward(val)); +} + +template + requires(std::is_convertible::value || + std::is_same::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 + requires(!std::is_convertible::value && !require_custom_serialization) +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>) { + 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 - template - simdjson_inline value::operator T() noexcept(false) { - return get(); - } - 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(); - } +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 value::begin() & noexcept { - return get_array().begin(); - } - simdjson_inline simdjson_result value::end() & noexcept { - return {}; - } - simdjson_inline simdjson_result value::count_elements() & noexcept { - simdjson_result 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 value::count_fields() & noexcept { - simdjson_result answer; - auto a = get_object(); - answer = a.count_fields(); - iter.move_at_start(); - return answer; - } - simdjson_inline simdjson_result value::at(size_t index) noexcept { - auto a = get_array(); - return a.at(index); - } +simdjson_inline simdjson_result +string_builder::view() const noexcept { + if (!is_valid) { + return simdjson::OUT_OF_CAPACITY; + } + return std::string_view(buffer.get(), position); +} - simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { - return start_or_resume_object().find_field(key); - } - simdjson_inline simdjson_result value::find_field(const char* key) noexcept { - return start_or_resume_object().find_field(key); - } +simdjson_inline simdjson_result string_builder::c_str() noexcept { + if (capacity_check(1)) { + buffer.get()[position] = '\0'; + return buffer.get(); + } + return simdjson::OUT_OF_CAPACITY; +} - simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { - return start_or_resume_object().find_field_unordered(key); - } - simdjson_inline simdjson_result value::find_field_unordered(const char* key) noexcept { - return start_or_resume_object().find_field_unordered(key); - } +simdjson_inline bool string_builder::validate_unicode() const noexcept { + return simdjson::validate_utf8(buffer.get(), position); +} - simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { - return start_or_resume_object()[key]; - } - simdjson_inline simdjson_result value::operator[](const char* key) noexcept { - return start_or_resume_object()[key]; - } +simdjson_inline void string_builder::start_object() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '{'; + } +} - simdjson_inline simdjson_result value::type() noexcept { - return iter.type(); - } +simdjson_inline void string_builder::end_object() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '}'; + } +} - simdjson_inline simdjson_result 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 void string_builder::start_array() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '['; + } +} - simdjson_inline simdjson_result value::is_string() noexcept { - json_type this_type; - auto error = type().get(this_type); - if (error) { return error; } - return (this_type == json_type::string); - } +simdjson_inline 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 bool value::is_negative() noexcept { - return iter.is_negative(); - } +simdjson_inline void string_builder::append_colon() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = ':'; + } +} - simdjson_inline simdjson_result value::is_integer() noexcept { - return iter.is_integer(); - } - simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { - return iter.get_number_type(); - } - simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { - return iter.get_number(); - } +template +simdjson_inline void +string_builder::append_key_value(key_type key, value_type value) noexcept { + static_assert(std::is_same::value || + std::is_convertible::value, + "Unsupported key type"); + escape_and_append_with_quotes(key); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(value); + } +} - simdjson_inline std::string_view value::raw_json_token() noexcept { - return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); - } +#if SIMDJSON_SUPPORTS_CONCEPTS +template +simdjson_inline void +string_builder::append_key_value(value_type value) noexcept { + escape_and_append_with_quotes(); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(value); + } +} +#endif - simdjson_inline simdjson_result 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 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::at_pointer(std::string_view json_pointer) noexcept { - 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::at_path(std::string_view json_path) noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) { - case json_type::array: - return (*this).get_array().at_path(json_path); - case json_type::object: - return (*this).get_object().at_path(json_path); - default: - return INVALID_JSON_POINTER; - } - } - - } // namespace ondemand - } // namespace icelake +} // namespace builder +} // namespace icelake } // namespace simdjson -namespace simdjson { +#endif // SIMDJSON_GENERIC_STRING_BUILDER_INL_H +/* end file simdjson/generic/builder/json_string_builder-inl.h for icelake */ - simdjson_inline simdjson_result::simdjson_result( - icelake::ondemand::value&& value - ) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) - { - } - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept : - implementation_simdjson_result_base(error) - { - } - simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); - } - simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); - } - simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() & noexcept { - if (error()) { return error(); } - return {}; - } - - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::find_field(const char* key) noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char* key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](const char* key) noexcept { - if (error()) { return error(); } - return first[key]; - } - - simdjson_inline simdjson_result simdjson_result::get_array() noexcept { - if (error()) { return error(); } - return first.get_array(); - } - simdjson_inline simdjson_result simdjson_result::get_object() noexcept { - if (error()) { return error(); } - return first.get_object(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); - } - simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); - } - simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); - } - template - simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(receiver, allow_replacement); - } - simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); - } - simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); - } - simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); - } - simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); - } - - template simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return first.get(); - } - template simdjson_inline error_code simdjson_result::get(T& out) noexcept { - if (error()) { return error(); } - return first.get(out); - } - - template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return std::move(first); - } - template<> simdjson_inline error_code simdjson_result::get(icelake::ondemand::value& out) noexcept { - if (error()) { return error(); } - out = first; - return SUCCESS; - } - - simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); - } - simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); - } - simdjson_inline simdjson_result simdjson_result::is_string() noexcept { - if (error()) { return error(); } - return first.is_string(); - } - simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); - } - simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); - } - simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); - } - simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); - } -#if SIMDJSON_EXCEPTIONS - template - simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return static_cast(first); - } - simdjson_inline simdjson_result::operator icelake::ondemand::array() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator icelake::ondemand::object() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator icelake::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } -#endif - - simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); - } - - simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { - if (error()) { return error(); } - return first.raw_json(); - } - - simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); - } - - simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { - if (error()) { return error(); } - return first.current_depth(); - } - - simdjson_inline simdjson_result simdjson_result::at_pointer( - std::string_view json_pointer) noexcept { - if (error()) { - return error(); - } - return first.at_pointer(json_pointer); - } - - simdjson_inline simdjson_result simdjson_result::at_path( - std::string_view json_path) noexcept { - if (error()) { - return error(); - } - return first.at_path(json_path); - } - -} // namespace simdjson - -#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H -/* end file simdjson/generic/ondemand/value-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 { - - 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 value_iterator::start_object() noexcept { - SIMDJSON_TRY(start_container('{', "Not an object", "object")); - return started_object(); - } - - simdjson_warn_unused simdjson_inline simdjson_result 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 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(); - 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 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 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 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 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 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_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 value_iterator::start_array() noexcept { - SIMDJSON_TRY(start_container('[', "Not an array", "array")); - return started_array(); - } - - simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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(!not_true); - } - simdjson_warn_unused simdjson_inline simdjson_result 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 value_iterator::get_string(bool allow_replacement) noexcept { - return get_raw_json_string().unescape(json_iter(), allow_replacement); - } - template - simdjson_warn_unused simdjson_inline error_code value_iterator::get_string(string_type& receiver, bool allow_replacement) noexcept { - std::string_view content; - auto err = get_string(allow_replacement).get(content); - if (err) { return err; } - receiver = content; - return SUCCESS; - } - simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_wobbly_string() noexcept { - return get_raw_json_string().unescape_wobbly(json_iter()); - } - simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_raw_json_string() noexcept { - auto json = peek_scalar("string"); - if (*json != '"') { return incorrect_type_error("Not a string"); } - advance_scalar("string"); - return raw_json_string(json + 1); - } - simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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 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 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 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 value_iterator::is_integer() noexcept { - return numberparsing::is_integer(peek_non_root_scalar("integer")); - } - simdjson_inline simdjson_result value_iterator::get_number_type() noexcept { - return numberparsing::get_number_type(peek_non_root_scalar("integer")); - } - simdjson_inline simdjson_result 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 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 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.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 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.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 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 - 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; - auto err = get_root_string(check_trailing, allow_replacement).get(content); - if (err) { return err; } - receiver = content; - return SUCCESS; - } - simdjson_warn_unused simdjson_inline simdjson_result 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 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; } - advance_scalar("string"); - return raw_json_string(json + 1); - } - simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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.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 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.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 value_iterator::get_root_bool(bool check_trailing) noexcept { - auto max_len = peek_root_length(); - auto json = peek_root_scalar("bool"); - uint8_t tmpbuf[5 + 1 + 1]; // +1 for null termination - tmpbuf[5 + 1] = '\0'; // make sure that buffer is always null terminated. - if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 5 + 1)) { return incorrect_type_error("Not a boolean"); } - auto result = parse_bool(tmpbuf); - if (result.error() == SUCCESS) { - if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; } - advance_root_scalar("bool"); - } - return result; - } - simdjson_inline simdjson_result 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"); - } - 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_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. - 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 simdjson_result value_iterator::reset_array() noexcept { - if (error()) { return error(); } - move_at_container_start(); - return started_array(); - } - - simdjson_inline simdjson_result 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 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 TAPE_ERROR; - } - } - - 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::simdjson_result(icelake::ondemand::value_iterator&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} - -} // namespace simdjson - -#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H -/* end file simdjson/generic/ondemand/value_iterator-inl.h for icelake */ -/* end file simdjson/generic/ondemand/amalgamated.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 */ @@ -74422,13 +47163,13 @@ SIMDJSON_UNTARGET_REGION #undef SIMDJSON_IMPLEMENTATION /* end file simdjson/icelake/end.h */ -#endif // SIMDJSON_ICELAKE_ONDEMAND_H -/* end file simdjson/icelake/ondemand.h */ +#endif // SIMDJSON_ICELAKE_BUILDER_H +/* end file simdjson/icelake/builder.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/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 */ @@ -74444,21 +47185,21 @@ SIMDJSON_UNTARGET_REGION /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - /** - * Implementation for ALTIVEC (PPC64). - */ - namespace ppc64 { +/** + * Implementation for ALTIVEC (PPC64). + */ +namespace ppc64 { - class implementation; +class implementation; - namespace { - namespace simd { - template struct simd8; - template struct simd8x64; - } // namespace simd - } // unnamed namespace +namespace { +namespace simd { +template struct simd8; +template struct simd8x64; +} // namespace simd +} // unnamed namespace - } // namespace ppc64 +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_PPC64_BASE_H @@ -74499,73 +47240,73 @@ static_assert(sizeof(__vector unsigned char) <= simdjson::SIMDJSON_PADDING, "ins /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { - namespace { +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) { +// 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; + 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); + 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 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) { +/* 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; + 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); + 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 - } +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); - } +simdjson_inline int count_ones(uint64_t input_num) { + return __builtin_popcountll(input_num); +} #endif - simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, - uint64_t* result) { +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { #if SIMDJSON_REGULAR_VISUAL_STUDIO - * result = value1 + value2; - return *result < value1; + *result = value1 + value2; + return *result < value1; #else - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); #endif - } +} - } // unnamed namespace - } // namespace ppc64 +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_PPC64_BITMANIPULATION_H @@ -74580,41 +47321,41 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { - namespace { +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; - } +// +// 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 +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif @@ -74639,51 +47380,57 @@ namespace simdjson { #endif namespace simdjson { - namespace ppc64 { - namespace numberparsing { +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)); +// 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); + val = bswap_64(val); #elif defined(__FreeBSD__) - val = bswap64(val); + val = bswap64(val); #endif #endif - val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8; - val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16; - return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32); - } + 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; +/** @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; + // 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 + 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); + __uint128_t r = (static_cast<__uint128_t>(value1)) * value2; + answer.low = uint64_t(r); + answer.high = uint64_t(r >> 64); #endif - return answer; - } + return answer; +} - } // namespace numberparsing - } // namespace ppc64 +} // 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 */ @@ -74701,469 +47448,464 @@ namespace simdjson { #include namespace simdjson { - namespace ppc64 { - namespace { - namespace simd { +namespace ppc64 { +namespace { +namespace simd { - using __m128i = __vector unsigned char; +using __m128i = __vector unsigned char; - template struct base { - __m128i value; +template struct base { + __m128i value; - // Zero constructor - simdjson_inline base() : value{ __m128i() } {} + // Zero constructor + simdjson_inline base() : value{__m128i()} {} - // Conversion from SIMD register - simdjson_inline base(const __m128i _value) : value(_value) {} + // 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; } + // 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(this); - *this_cast = *this_cast | other; - return *this_cast; - } - simdjson_inline Child& operator&=(const Child other) { - auto this_cast = static_cast(this); - *this_cast = *this_cast & other; - return *this_cast; - } - simdjson_inline Child& operator^=(const Child other) { - auto this_cast = static_cast(this); - *this_cast = *this_cast ^ other; - return *this_cast; - } - }; + // 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(this); + *this_cast = *this_cast | other; + return *this_cast; + } + simdjson_inline Child &operator&=(const Child other) { + auto this_cast = static_cast(this); + *this_cast = *this_cast & other; + return *this_cast; + } + simdjson_inline Child &operator^=(const Child other) { + auto this_cast = static_cast(this); + *this_cast = *this_cast ^ other; + return *this_cast; + } +}; - template > - struct base8 : base> { - typedef uint16_t bitmask_t; - typedef uint32_t bitmask2_t; +template > +struct base8 : base> { + typedef uint16_t bitmask_t; + typedef uint32_t bitmask2_t; - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m128i _value) : base>(_value) {} + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m128i _value) : base>(_value) {} - friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { - return (__m128i)vec_cmpeq(lhs.value, (__m128i)rhs); - } + friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { + return (__m128i)vec_cmpeq(lhs.value, (__m128i)rhs); + } - static const int SIZE = sizeof(base>::value); + static const int SIZE = sizeof(base>::value); - template - simdjson_inline simd8 prev(simd8 prev_chunk) const { - __m128i chunk = this->value; + template + simdjson_inline simd8 prev(simd8 prev_chunk) const { + __m128i chunk = this->value; #ifdef __LITTLE_ENDIAN__ - chunk = (__m128i)vec_reve(this->value); - prev_chunk = (__m128i)vec_reve((__m128i)prev_chunk); + 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); + chunk = (__m128i)vec_sld((__m128i)prev_chunk, (__m128i)chunk, 16 - N); #ifdef __LITTLE_ENDIAN__ - chunk = (__m128i)vec_reve((__m128i)chunk); + chunk = (__m128i)vec_reve((__m128i)chunk); #endif - return chunk; - } - }; + return chunk; + } +}; - // SIMD byte mask type (returned by things like eq and gt) - template <> struct simd8 : base8 { - static simdjson_inline simd8 splat(bool _value) { - return (__m128i)vec_splats((unsigned char)(-(!!_value))); - } +// SIMD byte mask type (returned by things like eq and gt) +template <> struct simd8 : base8 { + static simdjson_inline simd8 splat(bool _value) { + return (__m128i)vec_splats((unsigned char)(-(!!_value))); + } - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m128i _value) - : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) - : base8(splat(_value)) {} + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m128i _value) + : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) + : base8(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 }; + 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)); + result = ((__vector unsigned long long)vec_vbpermq((__m128i)this->value, + (__m128i)perm_mask)); #ifdef __LITTLE_ENDIAN__ - return static_cast(result[1]); + return static_cast(result[1]); #else - return static_cast(result[0]); + return static_cast(result[0]); #endif - } - simdjson_inline bool any() const { - return !vec_all_eq(this->value, (__m128i)vec_splats(0)); - } - simdjson_inline simd8 operator~() const { - return this->value ^ (__m128i)splat(true); - } - }; + } + simdjson_inline bool any() const { + return !vec_all_eq(this->value, (__m128i)vec_splats(0)); + } + simdjson_inline simd8 operator~() const { + return this->value ^ (__m128i)splat(true); + } +}; - template struct base8_numeric : base8 { - static simdjson_inline simd8 splat(T value) { - (void)value; - return (__m128i)vec_splats(value); - } - static simdjson_inline simd8 zero() { return splat(0); } - static simdjson_inline simd8 load(const T values[16]) { - return (__m128i)(vec_vsx_ld(0, reinterpret_cast(values))); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdjson_inline simd8 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(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, - v14, v15); - } +template struct base8_numeric : base8 { + static simdjson_inline simd8 splat(T value) { + (void)value; + return (__m128i)vec_splats(value); + } + static simdjson_inline simd8 zero() { return splat(0); } + static simdjson_inline simd8 load(const T values[16]) { + return (__m128i)(vec_vsx_ld(0, reinterpret_cast(values))); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, + v14, v15); + } - simdjson_inline base8_numeric() : base8() {} - simdjson_inline base8_numeric(const __m128i _value) - : base8(_value) {} + simdjson_inline base8_numeric() : base8() {} + simdjson_inline base8_numeric(const __m128i _value) + : base8(_value) {} - // Store to array - simdjson_inline void store(T dst[16]) const { - vec_vsx_st(this->value, 0, reinterpret_cast<__m128i*>(dst)); - } + // 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 operator~() const { return *this ^ 0xFFu; } + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - // Addition/subtraction are the same for signed and unsigned - simdjson_inline simd8 operator+(const simd8 other) const { - return (__m128i)((__m128i)this->value + (__m128i)other); - } - simdjson_inline simd8 operator-(const simd8 other) const { - return (__m128i)((__m128i)this->value - (__m128i)other); - } - simdjson_inline simd8& operator+=(const simd8 other) { - *this = *this + other; - return *static_cast *>(this); - } - simdjson_inline simd8& operator-=(const simd8 other) { - *this = *this - other; - return *static_cast *>(this); - } + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { + return (__m128i)((__m128i)this->value + (__m128i)other); + } + simdjson_inline simd8 operator-(const simd8 other) const { + return (__m128i)((__m128i)this->value - (__m128i)other); + } + simdjson_inline simd8 &operator+=(const simd8 other) { + *this = *this + other; + return *static_cast *>(this); + } + simdjson_inline simd8 &operator-=(const simd8 other) { + *this = *this - other; + return *static_cast *>(this); + } - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior - // for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return (__m128i)vec_perm((__m128i)lookup_table, (__m128i)lookup_table, this->value); - } + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior + // for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 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 compress(uint32_t mask) - // would be sensible, but the AVX ISA makes this kind of approach difficult. - template - 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. + // 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 compress(uint32_t mask) + // would be sensible, but the AVX ISA makes this kind of approach difficult. + template + 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] - }; + __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); + __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 }); + // 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(pshufb_combine_table + pop1 * 8)); - __m128i answer = vec_perm(pruned, (__m128i)vec_splats(0), compactmask); - vec_vsx_st(answer, 0, reinterpret_cast<__m128i*>(output)); - } + // 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(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 - simdjson_inline simd8 - 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::repeat_16( - replace0, replace1, replace2, replace3, replace4, replace5, replace6, - replace7, replace8, replace9, replace10, replace11, replace12, - replace13, replace14, replace15)); - } - }; + template + simdjson_inline simd8 + 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::repeat_16( + replace0, replace1, replace2, replace3, replace4, replace5, replace6, + replace7, replace8, replace9, replace10, replace11, replace12, + replace13, replace14, replace15)); + } +}; - // Signed bytes - template <> struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) - : base8_numeric(_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 - 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(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, - v13, v14, v15); - } +// Signed bytes +template <> struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) + : base8_numeric(_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 + 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(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, + v13, v14, v15); + } - // Order-sensitive comparisons - simdjson_inline simd8 - max_val(const simd8 other) const { - return (__m128i)vec_max((__vector signed char)this->value, + // Order-sensitive comparisons + simdjson_inline simd8 + max_val(const simd8 other) const { + return (__m128i)vec_max((__vector signed char)this->value, (__vector signed char)(__m128i)other); - } - simdjson_inline simd8 - min_val(const simd8 other) const { - return (__m128i)vec_min((__vector signed char)this->value, + } + simdjson_inline simd8 + min_val(const simd8 other) const { + return (__m128i)vec_min((__vector signed char)this->value, (__vector signed char)(__m128i)other); - } - simdjson_inline simd8 - operator>(const simd8 other) const { - return (__m128i)vec_cmpgt((__vector signed char)this->value, - (__vector signed char)(__m128i)other); - } - simdjson_inline simd8 - operator<(const simd8 other) const { - return (__m128i)vec_cmplt((__vector signed char)this->value, - (__vector signed char)(__m128i)other); - } - }; + } + simdjson_inline simd8 + operator>(const simd8 other) const { + return (__m128i)vec_cmpgt((__vector signed char)this->value, + (__vector signed char)(__m128i)other); + } + simdjson_inline simd8 + operator<(const simd8 other) const { + return (__m128i)vec_cmplt((__vector signed char)this->value, + (__vector signed char)(__m128i)other); + } +}; - // Unsigned bytes - template <> struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) - : base8_numeric(_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 - 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(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, - v13, v14, v15); - } +// Unsigned bytes +template <> struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) + : base8_numeric(_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 + 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(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, + v13, v14, v15); + } - // Saturated math - simdjson_inline simd8 - saturating_add(const simd8 other) const { - return (__m128i)vec_adds(this->value, (__m128i)other); - } - simdjson_inline simd8 - saturating_sub(const simd8 other) const { - return (__m128i)vec_subs(this->value, (__m128i)other); - } + // Saturated math + simdjson_inline simd8 + saturating_add(const simd8 other) const { + return (__m128i)vec_adds(this->value, (__m128i)other); + } + simdjson_inline simd8 + saturating_sub(const simd8 other) const { + return (__m128i)vec_subs(this->value, (__m128i)other); + } - // Order-specific operations - simdjson_inline simd8 - max_val(const simd8 other) const { - return (__m128i)vec_max(this->value, (__m128i)other); - } - simdjson_inline simd8 - min_val(const simd8 other) const { - return (__m128i)vec_min(this->value, (__m128i)other); - } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 - gt_bits(const simd8 other) const { - return this->saturating_sub(other); - } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 - lt_bits(const simd8 other) const { - return other.saturating_sub(*this); - } - simdjson_inline simd8 - operator<=(const simd8 other) const { - return other.max_val(*this) == other; - } - simdjson_inline simd8 - operator>=(const simd8 other) const { - return other.min_val(*this) == other; - } - simdjson_inline simd8 - operator>(const simd8 other) const { - return this->gt_bits(other).any_bits_set(); - } - simdjson_inline simd8 - operator<(const simd8 other) const { - return this->gt_bits(other).any_bits_set(); - } + // Order-specific operations + simdjson_inline simd8 + max_val(const simd8 other) const { + return (__m128i)vec_max(this->value, (__m128i)other); + } + simdjson_inline simd8 + min_val(const simd8 other) const { + return (__m128i)vec_min(this->value, (__m128i)other); + } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 + gt_bits(const simd8 other) const { + return this->saturating_sub(other); + } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 + lt_bits(const simd8 other) const { + return other.saturating_sub(*this); + } + simdjson_inline simd8 + operator<=(const simd8 other) const { + return other.max_val(*this) == other; + } + simdjson_inline simd8 + operator>=(const simd8 other) const { + return other.min_val(*this) == other; + } + simdjson_inline simd8 + operator>(const simd8 other) const { + return this->gt_bits(other).any_bits_set(); + } + simdjson_inline simd8 + operator<(const simd8 other) const { + return this->gt_bits(other).any_bits_set(); + } - // Bit-specific operations - simdjson_inline simd8 bits_not_set() const { - return (__m128i)vec_cmpeq(this->value, (__m128i)vec_splats(uint8_t(0))); - } - simdjson_inline simd8 bits_not_set(simd8 bits) const { - return (*this & bits).bits_not_set(); - } - simdjson_inline simd8 any_bits_set() const { - return ~this->bits_not_set(); - } - simdjson_inline simd8 any_bits_set(simd8 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 bits) const { - return vec_all_eq(vec_and(this->value, (__m128i)bits), - (__m128i)vec_splats(0)); - } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { - return !bits_not_set_anywhere(bits); - } - template simdjson_inline simd8 shr() const { - return simd8( - (__m128i)vec_sr(this->value, (__m128i)vec_splat_u8(N))); - } - template simdjson_inline simd8 shl() const { - return simd8( - (__m128i)vec_sl(this->value, (__m128i)vec_splat_u8(N))); - } - }; + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { + return (__m128i)vec_cmpeq(this->value, (__m128i)vec_splats(uint8_t(0))); + } + simdjson_inline simd8 bits_not_set(simd8 bits) const { + return (*this & bits).bits_not_set(); + } + simdjson_inline simd8 any_bits_set() const { + return ~this->bits_not_set(); + } + simdjson_inline simd8 any_bits_set(simd8 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 bits) const { + return vec_all_eq(vec_and(this->value, (__m128i)bits), + (__m128i)vec_splats(0)); + } + simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { + return !bits_not_set_anywhere(bits); + } + template simdjson_inline simd8 shr() const { + return simd8( + (__m128i)vec_sr(this->value, (__m128i)vec_splat_u8(N))); + } + template simdjson_inline simd8 shl() const { + return simd8( + (__m128i)vec_sl(this->value, (__m128i)vec_splat_u8(N))); + } +}; - template struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 4, - "PPC64 kernel should use four registers per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; +template struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 4, + "PPC64 kernel should use four registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& - operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed + simd8x64(const simd8x64 &o) = delete; // no copy allowed + simd8x64 & + operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, - const simd8 chunk2, const simd8 chunk3) - : chunks{ chunk0, chunk1, chunk2, chunk3 } {} - simdjson_inline simd8x64(const T ptr[64]) - : chunks{ simd8::load(ptr), simd8::load(ptr + 16), - simd8::load(ptr + 32), simd8::load(ptr + 48) } {} + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, + const simd8 chunk2, const simd8 chunk3) + : chunks{chunk0, chunk1, chunk2, chunk3} {} + simdjson_inline simd8x64(const T ptr[64]) + : chunks{simd8::load(ptr), simd8::load(ptr + 16), + simd8::load(ptr + 32), simd8::load(ptr + 48)} {} - simdjson_inline void store(T ptr[64]) const { - this->chunks[0].store(ptr + sizeof(simd8) * 0); - this->chunks[1].store(ptr + sizeof(simd8) * 1); - this->chunks[2].store(ptr + sizeof(simd8) * 2); - this->chunks[3].store(ptr + sizeof(simd8) * 3); - } + simdjson_inline void store(T ptr[64]) const { + this->chunks[0].store(ptr + sizeof(simd8) * 0); + this->chunks[1].store(ptr + sizeof(simd8) * 1); + this->chunks[2].store(ptr + sizeof(simd8) * 2); + this->chunks[3].store(ptr + sizeof(simd8) * 3); + } - simdjson_inline simd8 reduce_or() const { - return (this->chunks[0] | this->chunks[1]) | - (this->chunks[2] | this->chunks[3]); - } + simdjson_inline simd8 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 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 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 mask = simd8::splat(m); - return simd8x64(this->chunks[0] == mask, this->chunks[1] == mask, - this->chunks[2] == mask, this->chunks[3] == mask) - .to_bitmask(); - } + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64(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& other) const { - return simd8x64(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 eq(const simd8x64 &other) const { + return simd8x64(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 mask = simd8::splat(m); - return simd8x64(this->chunks[0] <= mask, this->chunks[1] <= mask, - this->chunks[2] <= mask, this->chunks[3] <= mask) - .to_bitmask(); - } - }; // struct simd8x64 + simdjson_inline uint64_t lteq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64(this->chunks[0] <= mask, this->chunks[1] <= mask, + this->chunks[2] <= mask, this->chunks[3] <= mask) + .to_bitmask(); + } +}; // struct simd8x64 - } // namespace simd - } // unnamed namespace - } // namespace ppc64 +} // namespace simd +} // unnamed namespace +} // namespace ppc64 } // namespace simdjson #endif // SIMDJSON_PPC64_SIMD_INPUT_H @@ -75180,58 +47922,84 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace ppc64 { - namespace { +namespace ppc64 { +namespace { - using namespace simd; +using namespace simd; - // Holds backslashes and quotes locations. - struct backslash_and_quote { - public: - static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote - copy_and_find(const uint8_t* src, uint8_t* dst); +// 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); - } + 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 + 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 v0(src); - simd8 v1(src + sizeof(v0)); - v0.store(dst); - v1.store(dst + sizeof(v0)); +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 v0(src); + simd8 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(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); - return { - uint32_t(bs_and_quote), // bs_bits - uint32_t(bs_and_quote >> 32) // quote_bits - }; - } + // 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(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); + return { + uint32_t(bs_and_quote), // bs_bits + uint32_t(bs_and_quote >> 32) // quote_bits + }; +} - } // unnamed namespace - } // namespace ppc64 + +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 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 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 @@ -75239,10301 +48007,1674 @@ namespace simdjson { #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! +/* 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 -// 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 +/* 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_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; - - } // 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/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): #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 { - 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; +#if SIMDJSON_SUPPORTS_CONCEPTS - /** - * Denote that we're starting a document. - */ - simdjson_inline void start_document() noexcept; +namespace ppc64 { +namespace builder { + class string_builder; +}} - /** - * 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; +template +struct has_custom_serialization : std::false_type {}; - /** - * Tell whether the iterator is at the EOF mark - */ - simdjson_inline bool at_end() const noexcept; +inline constexpr struct serialize_tag { + template + constexpr void operator()(ppc64::builder::string_builder& b, T&& obj) const{ + return tag_invoke(*this, b, std::forward(obj)); + } - /** - * 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; +} serialize{}; +template +struct has_custom_serialization(), std::declval())) +>> : std::true_type {}; - /** - * 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 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 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 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). - */ - simdjson_warn_unused simdjson_inline simdjson_result 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). - */ - simdjson_warn_unused simdjson_inline simdjson_result 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 has_next_field() noexcept; - - /** - * Get the current field's key. - */ - simdjson_warn_unused simdjson_inline simdjson_result field_key() 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 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 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 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 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). - */ - simdjson_warn_unused simdjson_inline simdjson_result 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). - */ - simdjson_warn_unused simdjson_inline simdjson_result 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 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 get_string(bool allow_replacement) noexcept; - template - simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_int64() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_double() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_bool() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_null() noexcept; - simdjson_warn_unused simdjson_inline bool is_negative() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_integer() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_number() noexcept; - - simdjson_warn_unused simdjson_inline simdjson_result get_root_string(bool check_trailing, bool allow_replacement) noexcept; - template - 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 get_root_wobbly_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_raw_json_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64_in_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_int64(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_int64_in_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_double(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_double_in_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_bool(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline bool is_root_negative() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_root_integer(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_number_type(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_number(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_root_null(bool check_trailing) noexcept; - - 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 reset_array() noexcept; - /** - * Restarts an object iteration. - * @returns Whether the object has any fields (returns false for empty). - */ - simdjson_inline simdjson_result 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 parse_null(const uint8_t* json) const noexcept; - simdjson_inline simdjson_result 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_inline error_code start_container(uint8_t start_char, const char* incorrect_type_message, const char* type) noexcept; - 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 advance_to_value() noexcept; - - simdjson_inline error_code incorrect_type_error(const char* message) const noexcept; - 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; - /** @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_inline error_code report_error(error_code error, const char* message) noexcept; - - friend class document; - friend class object; - friend class array; - friend class value; - friend class field; - }; // value_iterator - - } // namespace ondemand - } // namespace ppc64 -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public ppc64::implementation_simdjson_result_base { - 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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -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, uint64_t, int64_t, double, bool - * - * You may use get_double(), get_bool(), get_uint64(), get_int64(), - * 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 simdjson_inline simdjson_result get() noexcept { - // 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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " 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."); - } - - /** - * Get this value as the given type. - * - * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool - * - * @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 simdjson_inline error_code get(T& out) noexcept; - - /** - * 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 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 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 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 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 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 get_int64_in_string() 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 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 get_double_in_string() noexcept; - - /** - * Cast this JSON value to a string. - * - * The string is guaranteed to be valid UTF-8. - * - * Equivalent to get(). - * - * Important: a value should be consumed once. Calling get_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 get_string(bool allow_replacement = false) noexcept; - - /** - * 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 - 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 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 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 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 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 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 - 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 signed 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(). - * - * @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 begin() & noexcept; - /** - * Sentinel representing the end of the array. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result 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 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 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 at(size_t index) noexcept; - /** - * Look up a field by name on an object (order-sensitive). - * - * 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 }`: - * - * ```c++ - * 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 find_field(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ - simdjson_inline simdjson_result 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. - * - * It is the default, however, 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 find_field_unordered(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result find_field_unordered(const char* key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](const char* key) noexcept; - - /** - * 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 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 is_scalar() noexcept; - /** - * Checks whether the value is a string. - * - * @returns true if the type is string - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". - */ - simdjson_inline simdjson_result is_string() noexcept; - - /** - * Checks whether the value is a negative number. - * - * @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 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 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 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 raw_json() noexcept; - - /** - * Returns the current location in the document if in bounds. - */ - simdjson_inline simdjson_result 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 at_pointer(std::string_view json_pointer) noexcept; - - /** - * Get the value associated with the given JSONPath expression. We only support - * JSONPath queries that trivially convertible to JSON Pointer queries: key - * names and array indices. - * - * @return The value associated with the given JSONPath expression, or: - * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - */ - simdjson_inline simdjson_result at_path(std::string_view at_path) noexcept; - - - protected: - /** - * Create a value. - */ - 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 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; - friend struct simdjson_result; - friend class field; - }; - - } // namespace ondemand - } // namespace ppc64 -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public ppc64::implementation_simdjson_result_base { - 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 get_array() noexcept; - simdjson_inline simdjson_result get_object() noexcept; - - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result is_null() noexcept; - - template simdjson_inline simdjson_result get() noexcept; - - template simdjson_inline error_code get(T& out) noexcept; - -#if SIMDJSON_EXCEPTIONS - template - 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 count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - - /** - * Look up a field by name on an object (order-sensitive). - * - * 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 }`: - * - * ```c++ - * 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 find_field(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ - simdjson_inline simdjson_result 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. - * - * It is the default, however, 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 find_field_unordered(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result find_field_unordered(const char* key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](const char* key) noexcept; - - /** - * Get the type of this JSON value. - * - * 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 type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_string() noexcept; - simdjson_inline simdjson_result is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - - /** @copydoc simdjson_inline std::string_view value::raw_json_token() const noexcept */ - simdjson_inline simdjson_result raw_json_token() noexcept; - simdjson_inline simdjson_result raw_json() noexcept; - - /** @copydoc simdjson_inline simdjson_result current_location() noexcept */ - simdjson_inline simdjson_result current_location() noexcept; - /** @copydoc simdjson_inline int32_t current_depth() const noexcept */ - simdjson_inline simdjson_result current_depth() const noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - }; - -} // namespace simdjson - -#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; +template +constexpr bool require_custom_serialization = has_custom_serialization::value; #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 - 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 - 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 (`{` `}` `[` `]` `,` `:` `""` `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 value_iterator; - friend class object; - template - friend simdjson_inline void logger::log_line(const json_iterator& iter, const char* title_prefix, const char* title, std::string_view detail, int delta, int depth_delta, logger::log_level level, Args&&... args) noexcept; - template - 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 : public ppc64::implementation_simdjson_result_base { - 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 }; - - 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; - - /** - * 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_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_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 unescape(raw_json_string in, bool allow_replacement) noexcept; - simdjson_inline simdjson_result unescape_wobbly(raw_json_string in) noexcept; - - simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept; - - 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 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 - 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 - 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 : public ppc64::implementation_simdjson_result_base { - 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 { - // 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 - 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 - 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& type) noexcept(false); -#endif - - } // namespace ondemand - } // namespace ppc64 -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public ppc64::implementation_simdjson_result_base { - 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; - - /** - * 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 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 unescape_wobbly(json_iterator& iter) const noexcept; - const uint8_t* buf{}; - friend class object; - friend class field; - friend class parser; - friend struct simdjson_result; - }; - - 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 : public ppc64::implementation_simdjson_result_base { - 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 raw() const noexcept; - simdjson_inline simdjson_warn_unused simdjson_result unescape(ppc64::ondemand::json_iterator& iter, bool allow_replacement) const noexcept; - simdjson_inline simdjson_warn_unused simdjson_result 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 - -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. - * - * @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 iterate(padded_string_view json) & noexcept; -#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON - simdjson_warn_unused simdjson_result iterate_allow_incomplete_json(padded_string_view json) & noexcept; -#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const char* json, size_t len, size_t capacity) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const uint8_t* json, size_t len, size_t capacity) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(std::string_view json, size_t capacity) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const std::string& json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(std::string& json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const simdjson_result& json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const simdjson_result& json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result 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 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. - * - * ### 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. - * @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 10MB, which has been a reasonable sweet spot in our tests. - * @param allow_comma_separated (defaults on false) This allows a mode where the documents are - * separated by commas instead of whitespace. It comes with a performance - * penalty because the entire document is indexed at once (and the document must be - * less than 4 GB), and there is no multithreading. In this mode, the batch_size parameter - * is effectively ignored, as it is set to at least the document size. - * @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 iterate_many(const uint8_t* buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ - inline simdjson_result iterate_many(const char* buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ - inline simdjson_result iterate_many(const std::string& s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - inline simdjson_result iterate_many(const std::string&& s, size_t batch_size, bool allow_comma_separated = false) = delete;// unsafe - /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ - inline simdjson_result iterate_many(const padded_string& s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - inline simdjson_result iterate_many(const padded_string&& s, size_t batch_size, bool allow_comma_separated = false) = delete;// unsafe - - /** @private We do not want to allow implicit conversion from C string to std::string. */ - simdjson_result iterate_many(const char* buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete; - - /** 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 }; -#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 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 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 - - private: - /** @private [for benchmarking access] The implementation to use */ - std::unique_ptr implementation{}; - size_t _capacity{ 0 }; - size_t _max_capacity; - size_t _max_depth{ DEFAULT_MAX_DEPTH }; - std::unique_ptr string_buf{}; -#if SIMDJSON_DEVELOPMENT_CHECKS - std::unique_ptr start_positions{}; -#endif - - friend class json_iterator; - friend class document_stream; - }; - - } // namespace ondemand - } // namespace ppc64 -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public ppc64::implementation_simdjson_result_base { - 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): #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. - */ - simdjson_inline simdjson_result begin() noexcept; - /** - * Sentinel representing the end of the array. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result 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. - * - * To check that an array is empty, it is more performant to use - * the is_empty() method. - */ - simdjson_inline simdjson_result 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 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 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 at_pointer(std::string_view json_pointer) noexcept; - - /** - * Get the value associated with the given JSONPath expression. We only support - * JSONPath queries that trivially convertible to JSON Pointer queries: key - * names and array indices. - * - * https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00 - * - * @return The value associated with the given JSONPath expression, or: - * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - */ - inline simdjson_result at_path(std::string_view json_path) noexcept; - - /** - * Consumes the array and returns a string_view instance corresponding to the - * array as represented in JSON. It points inside the original document. - */ - simdjson_inline simdjson_result 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 at(size_t index) noexcept; - protected: - /** - * Go to the end of the array, no matter where you are right now. - */ - 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 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 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 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{}; - - friend class value; - friend class document; - friend struct simdjson_result; - friend struct simdjson_result; - friend class array_iterator; - }; - - } // namespace ondemand - } // namespace ppc64 -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public ppc64::implementation_simdjson_result_base { - 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 begin() noexcept; - simdjson_inline simdjson_result end() noexcept; - inline simdjson_result count_elements() & noexcept; - inline simdjson_result is_empty() & noexcept; - inline simdjson_result reset() & noexcept; - simdjson_inline simdjson_result at(size_t index) noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - simdjson_inline simdjson_result raw_json() noexcept; - - }; - -} // 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 "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 exactly once per element. - * - ++ must be called exactly once in between each * (*, ++, *, ++, * ...) - */ - class array_iterator { - public: - /** Create a new, invalid array iterator. */ - simdjson_inline array_iterator() noexcept = default; - - // - // Iterator interface - // - - /** - * Get the current element. - * - * Part of the std::iterator interface. - */ - simdjson_inline simdjson_result 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; - - private: - value_iterator iter{}; - - simdjson_inline array_iterator(const value_iterator& iter) noexcept; - - friend class array; - friend class value; - friend struct simdjson_result; - }; - - } // namespace ondemand - } // namespace ppc64 -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public ppc64::implementation_simdjson_result_base { - public: - 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 operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. - simdjson_inline bool operator==(const simdjson_result&) const noexcept; - simdjson_inline bool operator!=(const simdjson_result&) const noexcept; - simdjson_inline simdjson_result& operator++() 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): #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 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 get_object() & noexcept; - /** - * Cast this JSON value to an unsigned integer. - * - * @returns A signed 64-bit integer. - * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. - */ - simdjson_inline simdjson_result get_uint64() noexcept; - /** - * Cast this JSON value (inside string) to an unsigned integer. - * - * @returns A signed 64-bit integer. - * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. - */ - simdjson_inline simdjson_result 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 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 get_int64_in_string() 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 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 get_double_in_string() noexcept; - /** - * 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 get_string(bool allow_replacement = false) noexcept; - /** - * 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 - 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 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 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 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 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 is_null() noexcept; - - /** - * Get this value as the given type. - * - * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool - * - * You may use get_double(), get_bool(), get_uint64(), get_int64(), - * 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 simdjson_inline simdjson_result get() & noexcept { - // 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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " 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."); - } - /** @overload template simdjson_result get() & noexcept */ - template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept { - // 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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " 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."); - } - - /** - * Get this value as the given type. - * - * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 simdjson_inline error_code get(T& out) & noexcept; - /** @overload template error_code get(T &out) & noexcept */ - template simdjson_deprecated simdjson_inline error_code get(T& out) && noexcept; - -#if SIMDJSON_EXCEPTIONS - /** - * Cast this JSON value to an instance of type T. The programmer is responsible for - * providing an implementation of get 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 - explicit simdjson_inline operator T() & noexcept(false); - template - 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 signed 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. - * - * @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); - /** - * 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. - */ - simdjson_inline simdjson_result 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 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 at(size_t index) & noexcept; - /** - * Begin array iteration. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result begin() & noexcept; - /** - * Sentinel representing the end of the array. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result end() & noexcept; - - /** - * Look up a field by name on an object (order-sensitive). - * - * 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 }`: - * - * ```c++ - * 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 find_field(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result 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. - * - * It is the default, however, 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 find_field_unordered(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result find_field_unordered(const char* key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](const char* key) & noexcept; - - /** - * 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). - * - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". - */ - simdjson_inline simdjson_result 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 is_scalar() noexcept; - - /** - * Checks whether the document is a string. - * - * @returns true if the type is string - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". - */ - simdjson_inline simdjson_result is_string() noexcept; - - /** - * Checks whether the document is a negative number. - * - * @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 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 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 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 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 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 at_pointer(std::string_view json_pointer) noexcept; - - /** - * Get the value associated with the given JSONPath expression. We only support - * JSONPath queries that trivially convertible to JSON Pointer queries: key - * names and array indices. - * - * https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00 - * - * Key values are matched exactly, without unescaping or Unicode normalization. - * We do a byte-by-byte comparison. E.g. - * - * const padded_string json = "{\"\\u00E9\":123}"_padded; - * auto doc = parser.iterate(json); - * doc.at_path(".\\u00E9") == 123 - * doc.at_path((const char*)u8".\u00E9") returns an error (NO_SUCH_FIELD) - * - * @return The value associated with the given JSONPath expression, or: - * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - */ - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - - /** - * Consumes the document and returns a string_view instance corresponding to the - * document as represented in JSON. It points inside the original byte array containing - * the JSON document. - */ - simdjson_inline simdjson_result raw_json() noexcept; - protected: - /** - * Consumes the document. - */ - 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 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. - */ - 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 get_array() & noexcept; - simdjson_inline simdjson_result get_object() & noexcept; - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result get_value() noexcept; - - simdjson_inline simdjson_result is_null() noexcept; - template simdjson_inline simdjson_result get() & noexcept; - simdjson_inline simdjson_result raw_json() noexcept; - simdjson_inline operator document& () const noexcept; -#if SIMDJSON_EXCEPTIONS - template - explicit simdjson_inline operator T() noexcept(false); - simdjson_inline operator array() & noexcept(false); - simdjson_inline operator 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 raw_json_string() noexcept(false); - simdjson_inline operator bool() noexcept(false); - simdjson_inline operator value() noexcept(false); -#endif - simdjson_inline simdjson_result count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) & noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(const char* key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](const char* key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(const char* key) & noexcept; - - simdjson_inline simdjson_result type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_string() noexcept; - - simdjson_inline simdjson_result current_location() noexcept; - simdjson_inline int32_t current_depth() const noexcept; - simdjson_inline bool is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - simdjson_inline simdjson_result raw_json_token() noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - - private: - document* doc{ nullptr }; - }; - } // namespace ondemand - } // namespace ppc64 -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public ppc64::implementation_simdjson_result_base { - 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 get_array() & noexcept; - simdjson_inline simdjson_result get_object() & noexcept; - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result get_value() noexcept; - simdjson_inline simdjson_result is_null() noexcept; - - template simdjson_inline simdjson_result get() & noexcept; - template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept; - - template simdjson_inline error_code get(T& out) & noexcept; - template simdjson_inline error_code get(T& out) && noexcept; -#if SIMDJSON_EXCEPTIONS - template ::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); - 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); - simdjson_inline operator ppc64::ondemand::value() noexcept(false); -#endif - simdjson_inline simdjson_result count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) & noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(const char* key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](const char* key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(const char* key) & noexcept; - simdjson_inline simdjson_result type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_string() noexcept; - simdjson_inline simdjson_result current_location() noexcept; - simdjson_inline int32_t current_depth() const noexcept; - simdjson_inline bool at_end() const noexcept; - simdjson_inline bool is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - /** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */ - simdjson_inline simdjson_result raw_json_token() noexcept; - - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - }; - - -} // namespace simdjson - - - -namespace simdjson { - - template<> - struct simdjson_result : public ppc64::implementation_simdjson_result_base { - 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 get_array() & noexcept; - simdjson_inline simdjson_result get_object() & noexcept; - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result get_value() noexcept; - simdjson_inline simdjson_result is_null() noexcept; -#if SIMDJSON_EXCEPTIONS - template ::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); - 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); - simdjson_inline operator ppc64::ondemand::value() noexcept(false); -#endif - simdjson_inline simdjson_result count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) & noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(const char* key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](const char* key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(const char* key) & noexcept; - simdjson_inline simdjson_result type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_string() noexcept; - simdjson_inline simdjson_result current_location() noexcept; - simdjson_inline simdjson_result current_depth() const noexcept; - simdjson_inline simdjson_result is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - /** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */ - simdjson_inline simdjson_result raw_json_token() noexcept; - - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - }; - - -} // 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 -#include -#include -#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. - * - * ```c++ - * 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(); - * - */ - inline size_t truncated_bytes() const noexcept; - - class iterator { - public: - using value_type = simdjson_result; - using reference = simdjson_result; - using pointer = void; - 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 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. - * - * 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; - - 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; - }; - - /** - * 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) - */ - simdjson_inline document_stream( - ondemand::parser& parser, - const uint8_t* buf, - size_t len, - size_t batch_size, - bool allow_comma_separated - ) 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. */ - inline void next_document() 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; - /** - * 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 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. - */ - 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; - friend struct simdjson::internal::simdjson_result_base; - }; // document_stream - - } // namespace ondemand - } // namespace ppc64 -} // namespace simdjson - -namespace simdjson { - template<> - struct simdjson_result : public ppc64::implementation_simdjson_result_base { - 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 so you can use C++ algorithms that rely on pairs. - */ - class field : public std::pair { - 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 unescaped_key(bool allow_replacement = false) 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. 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 - 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; - /** - * 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 start(value_iterator& parent_iter) noexcept; - static simdjson_inline simdjson_result start(const value_iterator& parent_iter, raw_json_string key) noexcept; - friend struct simdjson_result; - friend class object_iterator; - }; - - } // namespace ondemand - } // namespace ppc64 -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public ppc64::implementation_simdjson_result_base { - 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 unescaped_key(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code unescaped_key(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result key() noexcept; - simdjson_inline simdjson_result key_raw_json_token() noexcept; - simdjson_inline simdjson_result escaped_key() noexcept; - simdjson_inline simdjson_result value() noexcept; - }; - -} // namespace simdjson - -#endif // SIMDJSON_GENERIC_ONDEMAND_FIELD_H -/* end file simdjson/generic/ondemand/field.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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -namespace simdjson { - namespace ppc64 { - namespace ondemand { - - /** - * 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; - - simdjson_inline simdjson_result begin() noexcept; - simdjson_inline simdjson_result end() noexcept; - /** - * Look up a field by name on an object (order-sensitive). - * - * 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 }`: - * - * ```c++ - * 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 }`. - * - * 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 find_field(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result 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. - * - * It is the default, however, 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 find_field_unordered(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) && 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("/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 at_pointer(std::string_view json_pointer) noexcept; - - /** - * Get the value associated with the given JSONPath expression. We only support - * JSONPath queries that trivially convertible to JSON Pointer queries: key - * names and array indices. - * - * @return The value associated with the given JSONPath expression, or: - * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - */ - inline simdjson_result at_path(std::string_view json_path) noexcept; - - /** - * Reset the iterator so that we are pointing back at the - * beginning of the object. You should still consume values only once even if you - * 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 reset() & 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 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 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 raw_json() noexcept; - - protected: - /** - * Go to the end of the object, no matter where you are right now. - */ - simdjson_inline error_code consume() noexcept; - static simdjson_inline simdjson_result start(value_iterator& iter) noexcept; - static simdjson_inline simdjson_result start_root(value_iterator& iter) noexcept; - static simdjson_inline simdjson_result 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{}; - - friend class value; - friend class document; - friend struct simdjson_result; - }; - - } // namespace ondemand - } // namespace ppc64 -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public ppc64::implementation_simdjson_result_base { - 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 begin() noexcept; - simdjson_inline simdjson_result end() noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) && noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - - inline simdjson_result reset() noexcept; - inline simdjson_result is_empty() noexcept; - inline simdjson_result count_fields() & noexcept; - inline simdjson_result raw_json() noexcept; - - }; - -} // 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; - - // - // Iterator interface - // - - // Reads key and value, yielding them to the user. - // MUST ONLY BE CALLED ONCE PER ITERATION. - simdjson_inline simdjson_result 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 ',' - simdjson_inline object_iterator& operator++() noexcept; - - private: - /** - * 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; - friend class object; - }; - - } // namespace ondemand - } // namespace ppc64 -} // namespace simdjson - -namespace simdjson { - - template<> - struct simdjson_result : public ppc64::implementation_simdjson_result_base { - 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 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&) const noexcept; - // Assumes it's being compared with the end. true if depth >= iter->depth. - simdjson_inline bool operator!=(const simdjson_result&) const noexcept; - // Checks for ']' and ',' - simdjson_inline simdjson_result& 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/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 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 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 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 to_json_string(ppc64::ondemand::array& x) noexcept; - inline simdjson_result to_json_string(simdjson_result x); - inline simdjson_result to_json_string(simdjson_result x); - inline simdjson_result to_json_string(simdjson_result x); - inline simdjson_result to_json_string(simdjson_result x); -} // namespace simdjson +struct has_custom_serialization : std::false_type {}; +#endif // SIMDJSON_SUPPORTS_CONCEPTS +namespace ppc64 { +namespace builder { /** - * 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 + * 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. */ -namespace simdjson { - namespace ppc64 { - namespace ondemand { +class string_builder { +public: + simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY); - /** - * 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); + 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::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 + 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 + simdjson_inline void append_key_value(key_type key, value_type value) noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + template + simdjson_inline void append_key_value(value_type value) noexcept; + + // Support for optional types (std::optional, etc.) + template + requires(!require_custom_serialization) + simdjson_inline void append(const T &opt); + + template + requires(require_custom_serialization) + simdjson_inline void append(T &&val); + + // Support for string-like types + template + requires(std::is_convertible::value || + std::is_same::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 +requires (!std::is_convertible::value && !require_custom_serialization) + 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; #if SIMDJSON_EXCEPTIONS - inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result 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 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&& 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&& 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 x); -#endif - } - } -} // namespace simdjson::ppc64::ondemand - -#endif // SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H -/* end file simdjson/generic/ondemand/serialization.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/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::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::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::started(value_iterator& iter) noexcept { - bool has_value; - SIMDJSON_TRY(iter.started_array().get(has_value)); - return array(iter); - } - - simdjson_inline simdjson_result array::begin() noexcept { -#if SIMDJSON_DEVELOPMENT_CHECKS - if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } -#endif - return array_iterator(iter); - } - simdjson_inline simdjson_result array::end() noexcept { - return array_iterator(iter); - } - 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 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(starting_point), size_t(final_point - starting_point)); - } - - SIMDJSON_PUSH_DISABLE_WARNINGS - SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING - simdjson_inline simdjson_result array::count_elements() & 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 array after counting the number of elements. - iter.reset_array(); - return count; - } - SIMDJSON_POP_DISABLE_WARNINGS - - simdjson_inline simdjson_result array::is_empty() & noexcept { - bool is_not_empty; - auto error = iter.reset_array().get(is_not_empty); - if (error) { return error; } - return !is_not_empty; - } - - inline simdjson_result array::reset() & noexcept { - return iter.reset_array(); - } - - inline simdjson_result array::at_pointer(std::string_view json_pointer) noexcept { - if (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; } - - // Read the array index - size_t array_index = 0; - size_t i; - for (i = 0; i < json_pointer.length() && json_pointer[i] != '/'; i++) { - uint8_t digit = uint8_t(json_pointer[i] - '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; } - array_index = array_index * 10 + digit; - } - - // 0 followed by other digits is invalid - if (i > 1 && json_pointer[0] == '0') { return INVALID_JSON_POINTER; } // "JSON pointer array index has other characters after 0" - - // Empty string is invalid; so is a "/" with no digits before it - if (i == 0) { return INVALID_JSON_POINTER; } // "Empty string in JSON pointer array index" - // 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 std::string json_path_to_pointer_conversion(std::string_view json_path) { - if (json_path.empty() || (json_path.front() != '.' && - json_path.front() != '[')) { - return "-1"; // This is just a sentinel value, the caller should check for this and return an error. - } - - std::string result; - // Reserve space to reduce allocations, adjusting for potential increases due - // to escaping. - result.reserve(json_path.size() * 2); - - size_t i = 0; - - while (i < json_path.length()) { - if (json_path[i] == '.') { - result += '/'; - } - else if (json_path[i] == '[') { - result += '/'; - ++i; // Move past the '[' - while (i < json_path.length() && json_path[i] != ']') { - if (json_path[i] == '~') { - result += "~0"; - } - else if (json_path[i] == '/') { - result += "~1"; - } - else { - result += json_path[i]; - } - ++i; - } - if (i == json_path.length() || json_path[i] != ']') { - return "-1"; // Using sentinel value that will be handled as an error by the caller. - } - } - else { - if (json_path[i] == '~') { - result += "~0"; - } - else if (json_path[i] == '/') { - result += "~1"; - } - else { - result += json_path[i]; - } - } - ++i; - } - - return result; - } - - inline simdjson_result array::at_path(std::string_view json_path) noexcept { - auto json_pointer = json_path_to_pointer_conversion(json_path); - if (json_pointer == "-1") { return INVALID_JSON_POINTER; } - return at_pointer(json_pointer); - } - - simdjson_inline simdjson_result array::at(size_t index) noexcept { - size_t i = 0; - for (auto value : *this) { - if (i == index) { return value; } - i++; - } - return INDEX_OUT_OF_BOUNDS; - } - - } // namespace ondemand - } // namespace ppc64 -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result( - ppc64::ondemand::array&& value - ) noexcept - : implementation_simdjson_result_base( - std::forward(value) - ) - { - } - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept - : implementation_simdjson_result_base(error) - { - } - - simdjson_inline simdjson_result simdjson_result::begin() noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() noexcept { - if (error()) { return error(); } - return first.end(); - } - simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); - } - simdjson_inline simdjson_result simdjson_result::is_empty() & noexcept { - if (error()) { return error(); } - return first.is_empty(); - } - simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } - simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { return error(); } - return first.at_path(json_path); - } - simdjson_inline simdjson_result simdjson_result::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 } - {} - - simdjson_inline simdjson_result array_iterator::operator*() noexcept { - 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 { - 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; - } - - } // namespace ondemand - } // namespace ppc64 -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result( - ppc64::ondemand::array_iterator&& value - ) noexcept - : ppc64::implementation_simdjson_result_base(std::forward(value)) - { - first.iter.assert_is_valid(); - } - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : ppc64::implementation_simdjson_result_base({}, error) - { - } - - simdjson_inline simdjson_result simdjson_result::operator*() noexcept { - if (error()) { return error(); } - return *first; - } - simdjson_inline bool simdjson_result::operator==(const simdjson_result& other) const noexcept { - if (!first.iter.is_valid()) { return !error(); } - return first == other.first; - } - simdjson_inline bool simdjson_result::operator!=(const simdjson_result& other) const noexcept { - if (!first.iter.is_valid()) { return error(); } - return first != other.first; - } - simdjson_inline simdjson_result& simdjson_result::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_ARRAY_ITERATOR_INL_H -/* end file simdjson/generic/ondemand/array_iterator-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/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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -namespace simdjson { - namespace ppc64 { - namespace ondemand { - - simdjson_inline document::document(ondemand::json_iterator&& _iter) noexcept - : iter{ std::forward(_iter) } - { - logger::log_start_value(iter, "document"); - } - - simdjson_inline document document::start(json_iterator&& iter) noexcept { - return document(std::forward(iter)); - } - - inline void document::rewind() noexcept { - iter.rewind(); - } - - inline std::string document::to_debug_string() noexcept { - return iter.to_string(); - } - - inline simdjson_result 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 document::start_or_resume_object() noexcept { - if (iter.at_root()) { - return get_object(); - } - else { - return object::resume(resume_value_iterator()); - } - } - simdjson_inline simdjson_result document::get_value() noexcept { - // Make sure we start any arrays or objects before returning, so that start_root_() - // 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 document::get_array() & noexcept { - auto value = get_root_value_iterator(); - return array::start_root(value); - } - simdjson_inline simdjson_result 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 document::get_uint64() noexcept { - return get_root_value_iterator().get_root_uint64(true); - } - simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { - return get_root_value_iterator().get_root_uint64_in_string(true); - } - simdjson_inline simdjson_result document::get_int64() noexcept { - return get_root_value_iterator().get_root_int64(true); - } - simdjson_inline simdjson_result document::get_int64_in_string() noexcept { - return get_root_value_iterator().get_root_int64_in_string(true); - } - simdjson_inline simdjson_result document::get_double() noexcept { - return get_root_value_iterator().get_root_double(true); - } - simdjson_inline simdjson_result document::get_double_in_string() noexcept { - return get_root_value_iterator().get_root_double_in_string(true); - } - simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { - return get_root_value_iterator().get_root_string(true, allow_replacement); - } - template - 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 document::get_wobbly_string() noexcept { - return get_root_value_iterator().get_root_wobbly_string(true); - } - simdjson_inline simdjson_result document::get_raw_json_string() noexcept { - return get_root_value_iterator().get_root_raw_json_string(true); - } - simdjson_inline simdjson_result document::get_bool() noexcept { - return get_root_value_iterator().get_root_bool(true); - } - simdjson_inline simdjson_result document::is_null() noexcept { - return get_root_value_iterator().is_root_null(true); - } - - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } - - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_value(); } - - template simdjson_inline error_code document::get(T& out) & noexcept { - return get().get(out); - } - template simdjson_deprecated simdjson_inline error_code document::get(T& out) && noexcept { - return std::forward(*this).get().get(out); - } - -#if SIMDJSON_EXCEPTIONS - template - simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } - template - simdjson_inline document::operator T() & noexcept(false) { return get(); } - 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) { return get_string(false); } - simdjson_inline document::operator raw_json_string() noexcept(false) { 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 document::count_elements() & noexcept { - auto a = get_array(); - simdjson_result 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 document::count_fields() & noexcept { - auto a = get_object(); - simdjson_result 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 document::at(size_t index) & noexcept { - auto a = get_array(); - return a.at(index); - } - simdjson_inline simdjson_result document::begin() & noexcept { - return get_array().begin(); - } - simdjson_inline simdjson_result document::end() & noexcept { - return {}; - } - - simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { - return start_or_resume_object().find_field(key); - } - simdjson_inline simdjson_result document::find_field(const char* key) & noexcept { - return start_or_resume_object().find_field(key); - } - simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { - return start_or_resume_object().find_field_unordered(key); - } - simdjson_inline simdjson_result document::find_field_unordered(const char* key) & noexcept { - return start_or_resume_object().find_field_unordered(key); - } - simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { - return start_or_resume_object()[key]; - } - simdjson_inline simdjson_result document::operator[](const char* key) & noexcept { - return start_or_resume_object()[key]; - } - - simdjson_inline error_code document::consume() noexcept { - auto error = iter.skip_child(0); - if (error) { iter.abandon(); } - return error; - } - - simdjson_inline simdjson_result 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(starting_point), size_t(final_point - starting_point)); - } - - simdjson_inline simdjson_result document::type() noexcept { - return get_root_value_iterator().type(); - } - - simdjson_inline simdjson_result document::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 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 document::is_integer() noexcept { - return get_root_value_iterator().is_root_integer(true); - } - - simdjson_inline simdjson_result document::get_number_type() noexcept { - return get_root_value_iterator().get_root_number_type(true); - } - - simdjson_inline simdjson_result document::get_number() noexcept { - return get_root_value_iterator().get_root_number(true); - } - - - simdjson_inline simdjson_result document::raw_json_token() noexcept { - auto _iter = get_root_value_iterator(); - return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); - } - - simdjson_inline simdjson_result 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 document::at_path(std::string_view json_path) noexcept { - rewind(); // Rewind the document each time at_pointer is called - if (json_path.empty()) { - return this->get_value(); - } - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) { - case json_type::array: - return (*this).get_array().at_path(json_path); - case json_type::object: - return (*this).get_object().at_path(json_path); - default: - return INVALID_JSON_POINTER; - } - } - - } // namespace ondemand - } // namespace ppc64 -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result( - ppc64::ondemand::document&& value - ) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) - { - } - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept : - implementation_simdjson_result_base( - error - ) - { - } - simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); - } - simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); - } - simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; - } - simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() & noexcept { - return {}; - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char* key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](const char* key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::find_field(const char* key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { - if (error()) { return error(); } - return first.get_array(); - } - simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { - if (error()) { return error(); } - return first.get_object(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); - } - simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); - } - simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); - } - template - simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(receiver, allow_replacement); - } - simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); - } - simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); - } - simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); - } - simdjson_inline simdjson_result simdjson_result::get_value() noexcept { - if (error()) { return error(); } - return first.get_value(); - } - simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); - } - - template - simdjson_inline simdjson_result simdjson_result::get() & noexcept { - if (error()) { return error(); } - return first.get(); - } - template - simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { - if (error()) { return error(); } - return std::forward(first).get(); - } - template - simdjson_inline error_code simdjson_result::get(T& out) & noexcept { - if (error()) { return error(); } - return first.get(out); - } - template - simdjson_inline error_code simdjson_result::get(T& out) && noexcept { - if (error()) { return error(); } - return std::forward(first).get(out); - } - - template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; - template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { - if (error()) { return error(); } - return std::forward(first); - } - template<> simdjson_inline error_code simdjson_result::get(ppc64::ondemand::document& out) & noexcept = delete; - template<> simdjson_inline error_code simdjson_result::get(ppc64::ondemand::document& out) && noexcept { - if (error()) { return error(); } - out = std::forward(first); - return SUCCESS; - } - - simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); - } - - simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); - } - - simdjson_inline simdjson_result simdjson_result::is_string() noexcept { - if (error()) { return error(); } - return first.is_string(); - } - - simdjson_inline bool simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); - } - - simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); - } - - simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); - } - - simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); - } - - -#if SIMDJSON_EXCEPTIONS - template ::value == false>::type> - simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator ppc64::ondemand::array() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator ppc64::ondemand::object() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator ppc64::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator ppc64::ondemand::value() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } + /** + * 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 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 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; + +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); + + /** + * 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. + */ + simdjson_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 buffer{}; + size_t position{0}; + size_t capacity{0}; + bool is_valid{true}; +}; - simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); - } - simdjson_inline bool simdjson_result::at_end() const noexcept { - if (error()) { return error(); } - return first.at_end(); - } - - - simdjson_inline int32_t simdjson_result::current_depth() const noexcept { - if (error()) { return error(); } - return first.current_depth(); - } - - simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); - } - - simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } - - simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { return error(); } - return first.at_path(json_path); - } - -} // namespace simdjson - - -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 document_reference::get_array() & noexcept { return doc->get_array(); } - simdjson_inline simdjson_result 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 document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } - simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } - simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } - simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } - simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } - simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } - simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } - template - 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 document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } - simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } - simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } - simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } - simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } -#if SIMDJSON_EXCEPTIONS - template - simdjson_inline document_reference::operator T() noexcept(false) { return get(); } - 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 document_reference::count_elements() & noexcept { return doc->count_elements(); } - simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } - simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } - simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } - simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } - simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } - simdjson_inline simdjson_result document_reference::find_field(const char* key) & noexcept { return doc->find_field(key); } - simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } - simdjson_inline simdjson_result document_reference::operator[](const char* key) & noexcept { return (*doc)[key]; } - simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } - simdjson_inline simdjson_result document_reference::find_field_unordered(const char* key) & noexcept { return doc->find_field_unordered(key); } - simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } - simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } - simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } - simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } - simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } - simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } - simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } - simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } - simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } - simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } - simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } - simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } - simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json(); } - simdjson_inline document_reference::operator document& () const noexcept { return *doc; } - - } // namespace ondemand - } // namespace ppc64 -} // namespace simdjson - - - -namespace simdjson { - simdjson_inline simdjson_result::simdjson_result(ppc64::ondemand::document_reference value, error_code error) - noexcept : implementation_simdjson_result_base(std::forward(value), error) {} - - - simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); - } - simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); - } - simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; - } - simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() & noexcept { - return {}; - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char* key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](const char* key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::find_field(const char* key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { - if (error()) { return error(); } - return first.get_array(); - } - simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { - if (error()) { return error(); } - return first.get_object(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); - } - simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); - } - simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); - } - template - simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(receiver, allow_replacement); - } - simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); - } - simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); - } - simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); - } - simdjson_inline simdjson_result simdjson_result::get_value() noexcept { - if (error()) { return error(); } - return first.get_value(); - } - simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); - } - simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); - } - simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); - } - simdjson_inline simdjson_result simdjson_result::is_string() noexcept { - if (error()) { return error(); } - return first.is_string(); - } - simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); - } - simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); - } - simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); - } - simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); - } -#if SIMDJSON_EXCEPTIONS - template ::value == false>::type> - simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator ppc64::ondemand::array() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator ppc64::ondemand::object() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator ppc64::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator ppc64::ondemand::value() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } -#endif - - simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); - } - - simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); - } - - simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } - - simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { - return error(); - } - return first.at_path(json_path); - } - -} // namespace simdjson - -#endif // SIMDJSON_GENERIC_ONDEMAND_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 -#include - -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 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 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 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 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 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 - ) 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 }, - 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 }, - allow_comma_separated{ false }, - 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 { - 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 document_stream::iterator::operator*() noexcept { - //if(stream->error) { return stream->error; } - return simdjson_result(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::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; } - doc_index = batch_start; - 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 - 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(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; - } - } - } - - inline void document_stream::next_document() noexcept { - // 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) { doc.iter.consume_character(','); } - // 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; - if (remaining <= batch_size) { - return p.implementation->stage1(&buf[_batch_start], remaining, stage1_mode::streaming_final); - } - else { - return p.implementation->stage1(&buf[_batch_start], batch_size, stage1_mode::streaming_partial); - } - } - - 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 { - auto depth = stream->doc.iter.depth(); - auto cur_struct_index = stream->doc.iter._root - stream->parser->implementation->structural_indexes.get(); - - // If at root, process the first token to determine if scalar value - if (stream->doc.iter.at_root()) { - 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 - // TODO: We could remove trailing whitespaces - // This returns a string spanning from start of value to the beginning of the next document (excluded) - { - auto next_index = 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(stream->buf) + current_index(); - while (svlen > 1 && (std::isspace(start[svlen - 1]) || start[svlen - 1] == '\0')) { - svlen--; - } - return std::string_view(start, svlen); - } - } - cur_struct_index++; - } - - while (cur_struct_index <= static_cast(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(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::simdjson_result( - error_code error - ) noexcept : - implementation_simdjson_result_base(error) - { - } - simdjson_inline simdjson_result::simdjson_result( - ppc64::ondemand::document_stream&& value - ) noexcept : - implementation_simdjson_result_base( - std::forward(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 + +#if !SIMDJSON_STATIC_REFLECTION +// fallback implementation until we have static reflection +template +simdjson_warn_unused simdjson_result 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 +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_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): #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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ +#if SIMDJSON_STATIC_REFLECTION -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() {} - - simdjson_inline field::field(raw_json_string key, ondemand::value&& value) noexcept - : std::pair(key, std::forward(value)) - { - } - - simdjson_inline simdjson_result 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::start(const value_iterator& parent_iter, raw_json_string key) noexcept { - return field(key, parent_iter.child()); - } - - simdjson_inline simdjson_warn_unused simdjson_result 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 answer = first.unescape(second.iter.json_iter(), allow_replacement); - first.consume(); - return answer; - } - - template - 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)); - 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(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(first.buf), end_quote - first.buf); - } - - simdjson_inline value& field::value() & noexcept { - return second; - } - - simdjson_inline value field::value() && noexcept { - return std::forward(*this).second; - } - - } // namespace ondemand - } // namespace ppc64 -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result( - ppc64::ondemand::field&& value - ) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) - { - } - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept : - implementation_simdjson_result_base(error) - { - } - - simdjson_inline simdjson_result simdjson_result::key() noexcept { - if (error()) { return error(); } - return first.key(); - } - - simdjson_inline simdjson_result simdjson_result::key_raw_json_token() noexcept { - if (error()) { return error(); } - return first.key_raw_json_token(); - } - - simdjson_inline simdjson_result simdjson_result::escaped_key() noexcept { - if (error()) { return error(); } - return first.escaped_key(); - } - - simdjson_inline simdjson_result simdjson_result::unescaped_key(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.unescaped_key(allow_replacement); - } - - template - simdjson_inline error_code simdjson_result::unescaped_key(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.unescaped_key(receiver, allow_replacement); - } - - simdjson_inline simdjson_result simdjson_result::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(other.token)), - parser{ other.parser }, - _string_buf_loc{ other._string_buf_loc }, - error{ other.error }, - _depth{ other._depth }, - _root{ other._root }, - _streaming{ other._streaming } - { - 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; - 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 } - - { - 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 '"': - if (*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(TAPE_ERROR, "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; - } - - 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 { -#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(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 json_iterator::current_location() const noexcept { - if (!is_alive()) { // Unrecoverable error - if (!at_root()) { - return reinterpret_cast(token.peek(-1)); - } - else { - return reinterpret_cast(token.peek()); - } - } - if (at_end()) { - return OUT_OF_BOUNDS; - } - return reinterpret_cast(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_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 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); - SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc)); - return result; -#else - return parser->unescape(in, _string_buf_loc, allow_replacement); -#endif - } - - simdjson_inline simdjson_result json_iterator::unescape_wobbly(raw_json_string in) noexcept { -#if SIMDJSON_DEVELOPMENT_CHECKS - auto result = parser->unescape_wobbly(in, _string_buf_loc); - SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc)); - 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_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_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::simdjson_result(ppc64::ondemand::json_iterator&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(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; - default: SIMDJSON_UNREACHABLE(); - } - return out; - } - -#if SIMDJSON_EXCEPTIONS - inline std::ostream& operator<<(std::ostream& out, simdjson_result& 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::simdjson_result(ppc64::ondemand::json_type&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(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 +#include #include +#include +#include +#include +#include +#include +#include +// #include // for std::define_static_string - header not available yet namespace simdjson { - namespace ppc64 { - namespace ondemand { - namespace logger { +namespace ppc64 { +namespace builder { - 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. +template + requires(concepts::container_but_not_string && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + auto it = t.begin(); + auto end = t.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} - // Helper to turn unprintable or newline characters into spaces - static inline char printable_char(char c) { - if (c >= 0x20) { - return c; - } - else { - return ' '; - } - } +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +constexpr void atom(string_builder &b, const T &t) { + b.escape_and_append_with_quotes(t); +} - template - 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_s); - if (size <= 0) return std::string(); - std::unique_ptr 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 - 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)...); - } - - template - 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)...); - 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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -namespace simdjson { - namespace ppc64 { - namespace ondemand { - - simdjson_inline simdjson_result 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(key.size()), key.data()); - return NO_SUCH_FIELD; - } - return value(iter.child()); - } - simdjson_inline simdjson_result 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(key.size()), key.data()); - return NO_SUCH_FIELD; - } - return value(iter.child()); - } - simdjson_inline simdjson_result object::operator[](const std::string_view key) & noexcept { - return find_field_unordered(key); - } - simdjson_inline simdjson_result object::operator[](const std::string_view key) && noexcept { - return std::forward(*this).find_field_unordered(key); - } - simdjson_inline simdjson_result object::find_field(const std::string_view key) & noexcept { - 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(key.size()), key.data()); - return NO_SUCH_FIELD; - } - return value(iter.child()); - } - simdjson_inline simdjson_result object::find_field(const std::string_view key) && noexcept { - 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(key.size()), key.data()); - return NO_SUCH_FIELD; - } - return value(iter.child()); - } - - simdjson_inline simdjson_result object::start(value_iterator& iter) noexcept { - SIMDJSON_TRY(iter.start_object().error()); - return object(iter); - } - simdjson_inline simdjson_result object::start_root(value_iterator& iter) noexcept { - SIMDJSON_TRY(iter.start_root_object().error()); - return object(iter); - } - 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 object::raw_json() noexcept { - 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(starting_point), size_t(final_point - starting_point)); - } - - simdjson_inline simdjson_result 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::begin() noexcept { -#if SIMDJSON_DEVELOPMENT_CHECKS - if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } -#endif - return object_iterator(iter); - } - simdjson_inline simdjson_result object::end() noexcept { - return object_iterator(iter); - } - - inline simdjson_result object::at_pointer(std::string_view json_pointer) noexcept { - if (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 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 object::at_path(std::string_view json_path) noexcept { - auto json_pointer = json_path_to_pointer_conversion(json_path); - if (json_pointer == "-1") { - return INVALID_JSON_POINTER; - } - return at_pointer(json_pointer); - } - - simdjson_inline simdjson_result object::count_fields() & noexcept { - size_t count{ 0 }; - // Important: we do not consume any of the values. - 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 object::is_empty() & noexcept { - 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 object::reset() & noexcept { - return iter.reset_object(); - } - - } // namespace ondemand - } // namespace ppc64 -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result(ppc64::ondemand::object&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} - - simdjson_inline simdjson_result simdjson_result::begin() noexcept { - if (error()) { return error(); } - return first.begin(); +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &m) { + if (m.empty()) { + b.append_raw("{}"); + return; + } + b.append('{'); + bool first = true; + for (const auto& [key, value] : m) { + if (!first) { + b.append(','); } - simdjson_inline simdjson_result simdjson_result::end() noexcept { - if (error()) { return error(); } - return first.end(); + first = false; + // Keys must be convertible to string_view per the concept + b.escape_and_append_with_quotes(key); + b.append(':'); + atom(b, value); + } + b.append('}'); +} + + +template::value && !std::is_same_v>::type> +constexpr void atom(string_builder &b, const number_type t) { + b.append(t); +} + +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, t.[:dm:]); + i++; + }; + b.append('}'); +} + +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &opt) { + if (opt) { + atom(b, opt.value()); + } else { + b.append_raw("null"); + } +} + +// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &ptr) { + if (ptr) { + atom(b, *ptr); + } else { + b.append_raw("null"); + } +} + +// Support for enums - serialize as string representation using expand approach from P2996R12 +template + requires(std::is_enum_v && !require_custom_serialization) +void atom(string_builder &b, const T &e) { +#if SIMDJSON_STATIC_REFLECTION + 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(std::meta::identifier_of(enum_val))); + if (e == [:enum_val:]) { + b.append_raw(enum_str); + return; } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) && noexcept { - if (error()) { return error(); } - return std::forward(first).find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) && noexcept { - if (error()) { return error(); } - return std::forward(first)[key]; - } - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) && noexcept { - if (error()) { return error(); } - return std::forward(first).find_field(key); - } - - simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } - - simdjson_inline simdjson_result simdjson_result::at_path( - std::string_view json_path) noexcept { - if (error()) { - return error(); - } - return first.at_path(json_path); - } - - inline simdjson_result simdjson_result::reset() noexcept { - if (error()) { return error(); } - return first.reset(); - } - - inline simdjson_result simdjson_result::is_empty() noexcept { - if (error()) { return error(); } - return first.is_empty(); - } - - simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); - } - - simdjson_inline simdjson_result simdjson_result::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 } - {} - - simdjson_inline simdjson_result object_iterator::operator*() noexcept { - 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 { - // 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::simdjson_result( - ppc64::ondemand::object_iterator&& value - ) noexcept - : implementation_simdjson_result_base(std::forward(value)) - { - first.iter.assert_is_valid(); - } - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base({}, error) - { - } - - simdjson_inline simdjson_result simdjson_result::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::operator==(const simdjson_result& 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::operator!=(const simdjson_result& other) const noexcept { - if (!first.iter.is_valid()) { return error(); } - return first != other.first; - } - // Checks for ']' and ',' - simdjson_inline simdjson_result& simdjson_result::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/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; - 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 parser::iterate(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } - - json.remove_utf8_bom(); - - // 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(json.data()), json.length(), stage1_mode::regular)); - return document::start({ reinterpret_cast(json.data()), this }); - } - -#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON - simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } - - json.remove_utf8_bom(); - - // 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(json.data()), json.length(), stage1_mode::regular); - if (err) { - if (err != UNCLOSED_STRING) - return err; - } - return document::start({ reinterpret_cast(json.data()), this, true }); - } -#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON - - simdjson_warn_unused simdjson_inline simdjson_result 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 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 parser::iterate(std::string_view json, size_t allocated) & noexcept { - return iterate(padded_string_view(json, allocated)); - } - - simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(std::string& json) & noexcept { - if (json.capacity() - json.size() < SIMDJSON_PADDING) { - json.reserve(json.size() + SIMDJSON_PADDING); - } - return iterate(padded_string_view(json)); - } - - simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const std::string& json) & noexcept { - return iterate(padded_string_view(json)); - } - - simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const simdjson_result& result) & noexcept { - // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 parser::iterate(const simdjson_result& result) & noexcept { - // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 parser::iterate_raw(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_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(json.data()), json.length(), stage1_mode::regular)); - return json_iterator(reinterpret_cast(json.data()), this); - } - - inline simdjson_result parser::iterate_many(const uint8_t* buf, size_t len, size_t batch_size, bool allow_comma_separated) 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 (allow_comma_separated && batch_size < len) { batch_size = len; } - return document_stream(*this, buf, len, batch_size, allow_comma_separated); - } - inline simdjson_result parser::iterate_many(const char* buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept { - return iterate_many(reinterpret_cast(buf), len, batch_size, allow_comma_separated); - } - inline simdjson_result parser::iterate_many(const std::string& s, size_t batch_size, bool allow_comma_separated) noexcept { - return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated); - } - inline simdjson_result parser::iterate_many(const padded_string& s, size_t batch_size, bool allow_comma_separated) noexcept { - return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated); - } - - 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 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(dst), end - dst); - dst = end; - return result; - } - - simdjson_inline simdjson_warn_unused simdjson_result 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(dst), end - dst); - dst = end; - return result; - } - - } // namespace ondemand - } // namespace ppc64 -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result(ppc64::ondemand::parser&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(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(buf); } - - - simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept { - size_t pos{ 0 }; - // if the content has no escape character, just scan through it quickly! - for (; pos < target.size() && target[pos] != '\\'; pos++) {} - // slow path may begin. - bool escaping{ false }; - for (; pos < target.size(); pos++) { - if ((target[pos] == '"') && !escaping) { - return false; - } - else if (target[pos] == '\\') { - escaping = !escaping; - } - else { - escaping = false; - } - } - return true; - } - - simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* target) noexcept { - size_t pos{ 0 }; - // if the content has no escape character, just scan through it quickly! - for (; target[pos] && target[pos] != '\\'; pos++) {} - // slow path may begin. - bool escaping{ false }; - for (; target[pos]; pos++) { - if ((target[pos] == '"') && !escaping) { - return false; - } - else if (target[pos] == '\\') { - escaping = !escaping; - } - else { - escaping = false; - } - } - 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()); - } - const char* r{ raw() }; - size_t pos{ 0 }; - for (; pos < target.size(); pos++) { - if (r[pos] != target[pos]) { return false; } - } - if (r[pos] != '"') { return false; } - return true; - } - - 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 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 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::simdjson_result(ppc64::ondemand::raw_json_string&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} - - simdjson_inline simdjson_result simdjson_result::raw() const noexcept { - if (error()) { return error(); } - return first.raw(); - } - simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::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 simdjson_result::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/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): #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 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 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 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 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 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 to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); - } - - inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); - } - - inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); - } - - inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); - } - - inline simdjson_result to_json_string(simdjson_result 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 x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); - } + }; + // Fallback to integer if enum value not found + atom(b, static_cast>(e)); #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); - } - } + // Fallback: serialize as integer if reflection not available + atom(b, static_cast>(e)); #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 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&& x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); - } - inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& 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 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 - } +// Support for appendable containers that don't have operator[] (sets, etc.) +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &container) { + if (container.empty()) { + b.append_raw("[]"); + return; + } + b.append('['); + bool first = true; + for (const auto& item : container) { + if (!first) { + b.append(','); } -} // namespace simdjson::ppc64::ondemand + first = false; + atom(b, item); + } + b.append(']'); +} -#endif // SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H -/* end file simdjson/generic/ondemand/serialization-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 +// append functions that delegate to atom functions for primitive types +template + requires(std::is_arithmetic_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +// works for struct +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^Z, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, z.[:dm:]); + i++; + }; + b.append('}'); +} + +// works for container that have begin() and end() iterators +template + requires(concepts::container_but_not_string && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + auto it = z.begin(); + auto end = z.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} + +template + requires (require_custom_serialization) +void append(string_builder &b, const Z &z) { + b.append(z); +} + + +template +simdjson_warn_unused simdjson_result to_json_string(const Z &z, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +template +simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + 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 +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 + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +void extract_from(string_builder &b, const T &obj) { + // 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) || ...); + }; + + b.append('{'); + bool first = true; + + // 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_public(mem)) { + 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 (should_extract(key)) { + if (!first) { + b.append(','); + } + first = false; + + // Serialize the key + constexpr auto quoted_key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem))); + b.append_raw(quoted_key); + b.append(':'); + + // Serialize the value + atom(b, obj.[:mem:]); + } + } + }; + + b.append('}'); +} + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + extract_from(b, obj); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +} // namespace builder +} // namespace ppc64 +// Alias the function template to 'to' in the global namespace +template +simdjson_warn_unused simdjson_result to_json(const Z &z, size_t initial_capacity = ppc64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + ppc64::builder::string_builder b(initial_capacity); + ppc64::builder::append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} +template +simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = ppc64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + ppc64::builder::string_builder b(initial_capacity); + ppc64::builder::append(b, z); + std::string_view view; + if(auto e = b.view().get(view); e) { return e; } + s.assign(view); + return SUCCESS; +} +// Global namespace function for extract_from +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result 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(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): #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): #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 */ -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 +#if SIMDJSON_STATIC_REFLECTION namespace simdjson { +namespace ppc64 { +namespace builder { - simdjson_inline simdjson_result::simdjson_result(ppc64::ondemand::token_iterator&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} +/** + * 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 +simdjson_warn_unused simdjson_result 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 + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result 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(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 +simdjson_warn_unused simdjson_result 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 + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result 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(obj, opts, initial_capacity); +} } // 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-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 +#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 +#include +#include +#ifndef SIMDJSON_GENERIC_STRING_BUILDER_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): #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 SIMDJSON_EXPERIMENTAL_HAS_NEON +#include +#ifdef _MSC_VER +#include +#endif +#endif +#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 +#include +#ifdef _MSC_VER +#include +#endif +#endif + namespace simdjson { - namespace ppc64 { - namespace ondemand { +namespace ppc64 { +namespace builder { - 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; - } +static SIMDJSON_CONSTEXPR_LAMBDA std::array + 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}; - simdjson_inline simdjson_result value::get_array() noexcept { - return array::start(iter); - } - simdjson_inline simdjson_result value::get_object() noexcept { - return object::start(iter); - } - simdjson_inline simdjson_result value::start_or_resume_object() noexcept { - if (iter.at_start()) { - return get_object(); - } - else { - return object::resume(iter); - } - } +/** - simdjson_inline simdjson_result value::get_raw_json_string() noexcept { - return iter.get_raw_json_string(); - } - simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { - return iter.get_string(allow_replacement); - } - template - 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 value::get_wobbly_string() noexcept { - return iter.get_wobbly_string(); - } - simdjson_inline simdjson_result value::get_double() noexcept { - return iter.get_double(); - } - simdjson_inline simdjson_result value::get_double_in_string() noexcept { - return iter.get_double_in_string(); - } - simdjson_inline simdjson_result value::get_uint64() noexcept { - return iter.get_uint64(); - } - simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { - return iter.get_uint64_in_string(); - } - simdjson_inline simdjson_result value::get_int64() noexcept { - return iter.get_int64(); - } - simdjson_inline simdjson_result value::get_int64_in_string() noexcept { - return iter.get_int64_in_string(); - } - simdjson_inline simdjson_result value::get_bool() noexcept { - return iter.get_bool(); - } - simdjson_inline simdjson_result value::is_null() noexcept { - return iter.is_null(); - } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } +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). - template simdjson_inline error_code value::get(T& out) noexcept { - return get().get(out); - } +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; +} + +**/ + +SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline bool +simple_needs_escaping(std::string_view v) { + for (char c : v) { + // a table lookup is faster than a series of comparisons + if (json_quotable_character[static_cast(c)]) { + return true; + } + } + return false; +} + +#if SIMDJSON_EXPERIMENTAL_HAS_NEON +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + uint8x16_t running = vdupq_n_u8(0); + uint8x16_t v34 = vdupq_n_u8(34); + uint8x16_t v92 = vdupq_n_u8(92); + + for (; i + 15 < view.size(); i += 16) { + uint8x16_t word = vld1q_u8((const uint8_t *)view.data() + i); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + if (i < view.size()) { + uint8x16_t word = + vld1q_u8((const uint8_t *)view.data() + view.length() - 16); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + return vmaxvq_u32(vreinterpretq_u32_u8(running)) != 0; +} +#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2 +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + __m128i running = _mm_setzero_si128(); + for (; i + 15 < view.size(); i += 16) { + + __m128i word = + _mm_loadu_si128(reinterpret_cast(view.data() + i)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + if (i < view.size()) { + __m128i word = _mm_loadu_si128( + reinterpret_cast(view.data() + view.length() - 16)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + return _mm_movemask_epi8(running) != 0; +} +#else +simdjson_inline bool fast_needs_escaping(std::string_view view) { + return simple_needs_escaping(view); +} +#endif + +// 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(*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(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)); + + if (vmaxvq_u32(vreinterpretq_u32_u8(needs_escape)) != 0) { + // Found quotable character - extract exact byte position using ctz + uint64x2_t as64 = vreinterpretq_u64_u8(needs_escape); + uint64_t lo = vgetq_lane_u64(as64, 0); + uint64_t hi = vgetq_lane_u64(as64, 1); + size_t offset = ptr - reinterpret_cast(view.data()); +#ifdef _MSC_VER + unsigned long trailing_zero = 0; + if (lo != 0) { + _BitScanForward64(&trailing_zero, lo); + return offset + trailing_zero / 8; + } else { + _BitScanForward64(&trailing_zero, hi); + return offset + 8 + trailing_zero / 8; + } +#else + if (lo != 0) { + return offset + __builtin_ctzll(lo) / 8; + } else { + return offset + 8 + __builtin_ctzll(hi) / 8; + } +#endif + } + 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(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(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(view.data()); +#ifdef _MSC_VER + unsigned long trailing_zero = 0; + _BitScanForward(&trailing_zero, mask); + return offset + trailing_zero; +#else + return offset + __builtin_ctz(mask); +#endif + } + 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(); + } +} + +// 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 out - initout; +} + +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; +} + +simdjson_inline void string_builder::grow_buffer(size_t desired_capacity) { + if (!is_valid) { + return; + } + std::unique_ptr 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 { + +template ::value>::type> +simdjson_really_inline int int_log2(number_type x) { + return 63 - leading_zeroes(uint64_t(x) | 1); +} + +simdjson_really_inline int fast_digit_count_32(uint32_t x) { + static uint64_t table[] = { + 4294967296, 8589934582, 8589934582, 8589934582, 12884901788, + 12884901788, 12884901788, 17179868184, 17179868184, 17179868184, + 21474826480, 21474826480, 21474826480, 21474826480, 25769703776, + 25769703776, 25769703776, 30063771072, 30063771072, 30063771072, + 34349738368, 34349738368, 34349738368, 34349738368, 38554705664, + 38554705664, 38554705664, 41949672960, 41949672960, 41949672960, + 42949672960, 42949672960}; + return uint32_t((x + table[int_log2(x)]) >> 32); +} + +simdjson_really_inline int fast_digit_count_64(uint64_t x) { + static uint64_t table[] = {9, + 99, + 999, + 9999, + 99999, + 999999, + 9999999, + 99999999, + 999999999, + 9999999999, + 99999999999, + 999999999999, + 9999999999999, + 99999999999999, + 999999999999999ULL, + 9999999999999999ULL, + 99999999999999999ULL, + 999999999999999999ULL, + 9999999999999999999ULL}; + int y = (19 * int_log2(x) >> 6); + y += x > table[y]; + return y + 1; +} + +template ::value>::type> +simdjson_really_inline size_t digit_count(number_type v) noexcept { + static_assert(sizeof(number_type) == 8 || sizeof(number_type) == 4 || + sizeof(number_type) == 2 || sizeof(number_type) == 1, + "We only support 8-bit, 16-bit, 32-bit and 64-bit numbers"); + SIMDJSON_IF_CONSTEXPR(sizeof(number_type) <= 4) { + return fast_digit_count_32(static_cast(v)); + } + else { + return fast_digit_count_64(static_cast(v)); + } +} +static const char decimal_table[200] = { + 0x30, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35, + 0x30, 0x36, 0x30, 0x37, 0x30, 0x38, 0x30, 0x39, 0x31, 0x30, 0x31, 0x31, + 0x31, 0x32, 0x31, 0x33, 0x31, 0x34, 0x31, 0x35, 0x31, 0x36, 0x31, 0x37, + 0x31, 0x38, 0x31, 0x39, 0x32, 0x30, 0x32, 0x31, 0x32, 0x32, 0x32, 0x33, + 0x32, 0x34, 0x32, 0x35, 0x32, 0x36, 0x32, 0x37, 0x32, 0x38, 0x32, 0x39, + 0x33, 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x33, 0x34, 0x33, 0x35, + 0x33, 0x36, 0x33, 0x37, 0x33, 0x38, 0x33, 0x39, 0x34, 0x30, 0x34, 0x31, + 0x34, 0x32, 0x34, 0x33, 0x34, 0x34, 0x34, 0x35, 0x34, 0x36, 0x34, 0x37, + 0x34, 0x38, 0x34, 0x39, 0x35, 0x30, 0x35, 0x31, 0x35, 0x32, 0x35, 0x33, + 0x35, 0x34, 0x35, 0x35, 0x35, 0x36, 0x35, 0x37, 0x35, 0x38, 0x35, 0x39, + 0x36, 0x30, 0x36, 0x31, 0x36, 0x32, 0x36, 0x33, 0x36, 0x34, 0x36, 0x35, + 0x36, 0x36, 0x36, 0x37, 0x36, 0x38, 0x36, 0x39, 0x37, 0x30, 0x37, 0x31, + 0x37, 0x32, 0x37, 0x33, 0x37, 0x34, 0x37, 0x35, 0x37, 0x36, 0x37, 0x37, + 0x37, 0x38, 0x37, 0x39, 0x38, 0x30, 0x38, 0x31, 0x38, 0x32, 0x38, 0x33, + 0x38, 0x34, 0x38, 0x35, 0x38, 0x36, 0x38, 0x37, 0x38, 0x38, 0x38, 0x39, + 0x39, 0x30, 0x39, 0x31, 0x39, 0x32, 0x39, 0x33, 0x39, 0x34, 0x39, 0x35, + 0x39, 0x36, 0x39, 0x37, 0x39, 0x38, 0x39, 0x39, +}; +} // namespace internal + +template +simdjson_inline void string_builder::append(number_type v) noexcept { + static_assert(std::is_same::value || + std::is_integral::value || + std::is_floating_point::value, + "Unsupported number type"); + // If C++17 is available, we can 'if constexpr' here. + SIMDJSON_IF_CONSTEXPR(std::is_same::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::value) { + // Process 4 digits at a time instead of 2, reducing store operations + // and divisions by approximately half for large numbers. + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + unsigned_type pv = static_cast(v); + size_t dc = internal::digit_count(pv); + char *write_pointer = buffer.get() + position + dc - 1; + + // Process 4 digits per iteration for large numbers + while (pv >= 10000) { + unsigned_type q = pv / 10000; + unsigned_type r = pv % 10000; + unsigned_type r_hi = r / 100; // High 2 digits of remainder + unsigned_type r_lo = r % 100; // Low 2 digits of remainder + // Write low 2 digits first (rightmost), then high 2 digits + memcpy(write_pointer - 1, &internal::decimal_table[r_lo * 2], 2); + memcpy(write_pointer - 3, &internal::decimal_table[r_hi * 2], 2); + write_pointer -= 4; + pv = q; + } + + // Handle remaining 1-4 digits with original 2-digit loop + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_integral::value) { + // Same 4-digit batching as unsigned path for signed integers + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + bool negative = v < 0; + unsigned_type pv = static_cast(v); + if (negative) { + pv = 0 - pv; // the 0 is for Microsoft + } + size_t dc = internal::digit_count(pv); + // by always writing the minus sign, we avoid the branch. + buffer.get()[position] = '-'; + position += negative ? 1 : 0; + char *write_pointer = buffer.get() + position + dc - 1; + + // Process 4 digits per iteration for large numbers + while (pv >= 10000) { + unsigned_type q = pv / 10000; + unsigned_type r = pv % 10000; + unsigned_type r_hi = r / 100; + unsigned_type r_lo = r % 100; + memcpy(write_pointer - 1, &internal::decimal_table[r_lo * 2], 2); + memcpy(write_pointer - 3, &internal::decimal_table[r_hi * 2], 2); + write_pointer -= 4; + pv = q; + } + + // Handle remaining 1-4 digits + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_floating_point::value) { + constexpr size_t max_number_size = 24; + if (capacity_check(max_number_size)) { + // 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. + 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. + 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 +simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept { + escape_and_append_with_quotes(constevalutil::string_constant::value); +} +#endif + +simdjson_inline void string_builder::append_raw(const char *c) noexcept { + size_t len = std::strlen(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; + } +} +#if SIMDJSON_SUPPORTS_CONCEPTS +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +simdjson_inline void string_builder::append(const T &opt) { + if (opt) { + append(*opt); + } else { + append_null(); + } +} + +template + requires(require_custom_serialization) +simdjson_inline void string_builder::append(T &&val) { + serialize(*this, std::forward(val)); +} + +template + requires(std::is_convertible::value || + std::is_same::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 + requires(!std::is_convertible::value && !require_custom_serialization) +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>) { + 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 - template - simdjson_inline value::operator T() noexcept(false) { - return get(); - } - 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(); - } +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 value::begin() & noexcept { - return get_array().begin(); - } - simdjson_inline simdjson_result value::end() & noexcept { - return {}; - } - simdjson_inline simdjson_result value::count_elements() & noexcept { - simdjson_result 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 value::count_fields() & noexcept { - simdjson_result answer; - auto a = get_object(); - answer = a.count_fields(); - iter.move_at_start(); - return answer; - } - simdjson_inline simdjson_result value::at(size_t index) noexcept { - auto a = get_array(); - return a.at(index); - } +simdjson_inline simdjson_result +string_builder::view() const noexcept { + if (!is_valid) { + return simdjson::OUT_OF_CAPACITY; + } + return std::string_view(buffer.get(), position); +} - simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { - return start_or_resume_object().find_field(key); - } - simdjson_inline simdjson_result value::find_field(const char* key) noexcept { - return start_or_resume_object().find_field(key); - } +simdjson_inline simdjson_result string_builder::c_str() noexcept { + if (capacity_check(1)) { + buffer.get()[position] = '\0'; + return buffer.get(); + } + return simdjson::OUT_OF_CAPACITY; +} - simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { - return start_or_resume_object().find_field_unordered(key); - } - simdjson_inline simdjson_result value::find_field_unordered(const char* key) noexcept { - return start_or_resume_object().find_field_unordered(key); - } +simdjson_inline bool string_builder::validate_unicode() const noexcept { + return simdjson::validate_utf8(buffer.get(), position); +} - simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { - return start_or_resume_object()[key]; - } - simdjson_inline simdjson_result value::operator[](const char* key) noexcept { - return start_or_resume_object()[key]; - } +simdjson_inline void string_builder::start_object() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '{'; + } +} - simdjson_inline simdjson_result value::type() noexcept { - return iter.type(); - } +simdjson_inline void string_builder::end_object() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '}'; + } +} - simdjson_inline simdjson_result 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 void string_builder::start_array() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = '['; + } +} - simdjson_inline simdjson_result value::is_string() noexcept { - json_type this_type; - auto error = type().get(this_type); - if (error) { return error; } - return (this_type == json_type::string); - } +simdjson_inline 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 bool value::is_negative() noexcept { - return iter.is_negative(); - } +simdjson_inline void string_builder::append_colon() noexcept { + if (capacity_check(1)) { + buffer.get()[position++] = ':'; + } +} - simdjson_inline simdjson_result value::is_integer() noexcept { - return iter.is_integer(); - } - simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { - return iter.get_number_type(); - } - simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { - return iter.get_number(); - } +template +simdjson_inline void +string_builder::append_key_value(key_type key, value_type value) noexcept { + static_assert(std::is_same::value || + std::is_convertible::value, + "Unsupported key type"); + escape_and_append_with_quotes(key); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(value); + } +} - simdjson_inline std::string_view value::raw_json_token() noexcept { - return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); - } +#if SIMDJSON_SUPPORTS_CONCEPTS +template +simdjson_inline void +string_builder::append_key_value(value_type value) noexcept { + escape_and_append_with_quotes(); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(value); + } +} +#endif - simdjson_inline simdjson_result 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 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::at_pointer(std::string_view json_pointer) noexcept { - 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::at_path(std::string_view json_path) noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) { - case json_type::array: - return (*this).get_array().at_path(json_path); - case json_type::object: - return (*this).get_object().at_path(json_path); - default: - return INVALID_JSON_POINTER; - } - } - - } // namespace ondemand - } // namespace ppc64 +} // namespace builder +} // namespace ppc64 } // namespace simdjson -namespace simdjson { +#endif // SIMDJSON_GENERIC_STRING_BUILDER_INL_H +/* end file simdjson/generic/builder/json_string_builder-inl.h for ppc64 */ - simdjson_inline simdjson_result::simdjson_result( - ppc64::ondemand::value&& value - ) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) - { - } - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept : - implementation_simdjson_result_base(error) - { - } - simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); - } - simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); - } - simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() & noexcept { - if (error()) { return error(); } - return {}; - } - - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::find_field(const char* key) noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char* key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](const char* key) noexcept { - if (error()) { return error(); } - return first[key]; - } - - simdjson_inline simdjson_result simdjson_result::get_array() noexcept { - if (error()) { return error(); } - return first.get_array(); - } - simdjson_inline simdjson_result simdjson_result::get_object() noexcept { - if (error()) { return error(); } - return first.get_object(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); - } - simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); - } - simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); - } - template - simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(receiver, allow_replacement); - } - simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); - } - simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); - } - simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); - } - simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); - } - - template simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return first.get(); - } - template simdjson_inline error_code simdjson_result::get(T& out) noexcept { - if (error()) { return error(); } - return first.get(out); - } - - template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return std::move(first); - } - template<> simdjson_inline error_code simdjson_result::get(ppc64::ondemand::value& out) noexcept { - if (error()) { return error(); } - out = first; - return SUCCESS; - } - - simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); - } - simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); - } - simdjson_inline simdjson_result simdjson_result::is_string() noexcept { - if (error()) { return error(); } - return first.is_string(); - } - simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); - } - simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); - } - simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); - } - simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); - } -#if SIMDJSON_EXCEPTIONS - template - simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return static_cast(first); - } - simdjson_inline simdjson_result::operator ppc64::ondemand::array() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator ppc64::ondemand::object() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator ppc64::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } -#endif - - simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); - } - - simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { - if (error()) { return error(); } - return first.raw_json(); - } - - simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); - } - - simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { - if (error()) { return error(); } - return first.current_depth(); - } - - simdjson_inline simdjson_result simdjson_result::at_pointer( - std::string_view json_pointer) noexcept { - if (error()) { - return error(); - } - return first.at_pointer(json_pointer); - } - - simdjson_inline simdjson_result simdjson_result::at_path( - std::string_view json_path) noexcept { - if (error()) { - return error(); - } - return first.at_path(json_path); - } - -} // namespace simdjson - -#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H -/* end file simdjson/generic/ondemand/value-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 { - - 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 value_iterator::start_object() noexcept { - SIMDJSON_TRY(start_container('{', "Not an object", "object")); - return started_object(); - } - - simdjson_warn_unused simdjson_inline simdjson_result 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 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(); - 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 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 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 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 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 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_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 value_iterator::start_array() noexcept { - SIMDJSON_TRY(start_container('[', "Not an array", "array")); - return started_array(); - } - - simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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(!not_true); - } - simdjson_warn_unused simdjson_inline simdjson_result 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 value_iterator::get_string(bool allow_replacement) noexcept { - return get_raw_json_string().unescape(json_iter(), allow_replacement); - } - template - simdjson_warn_unused simdjson_inline error_code value_iterator::get_string(string_type& receiver, bool allow_replacement) noexcept { - std::string_view content; - auto err = get_string(allow_replacement).get(content); - if (err) { return err; } - receiver = content; - return SUCCESS; - } - simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_wobbly_string() noexcept { - return get_raw_json_string().unescape_wobbly(json_iter()); - } - simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_raw_json_string() noexcept { - auto json = peek_scalar("string"); - if (*json != '"') { return incorrect_type_error("Not a string"); } - advance_scalar("string"); - return raw_json_string(json + 1); - } - simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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 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 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 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 value_iterator::is_integer() noexcept { - return numberparsing::is_integer(peek_non_root_scalar("integer")); - } - simdjson_inline simdjson_result value_iterator::get_number_type() noexcept { - return numberparsing::get_number_type(peek_non_root_scalar("integer")); - } - simdjson_inline simdjson_result 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 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 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.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 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.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 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 - 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; - auto err = get_root_string(check_trailing, allow_replacement).get(content); - if (err) { return err; } - receiver = content; - return SUCCESS; - } - simdjson_warn_unused simdjson_inline simdjson_result 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 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; } - advance_scalar("string"); - return raw_json_string(json + 1); - } - simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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.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 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.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 value_iterator::get_root_bool(bool check_trailing) noexcept { - auto max_len = peek_root_length(); - auto json = peek_root_scalar("bool"); - uint8_t tmpbuf[5 + 1 + 1]; // +1 for null termination - tmpbuf[5 + 1] = '\0'; // make sure that buffer is always null terminated. - if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 5 + 1)) { return incorrect_type_error("Not a boolean"); } - auto result = parse_bool(tmpbuf); - if (result.error() == SUCCESS) { - if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; } - advance_root_scalar("bool"); - } - return result; - } - simdjson_inline simdjson_result 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"); - } - 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_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. - 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 simdjson_result value_iterator::reset_array() noexcept { - if (error()) { return error(); } - move_at_container_start(); - return started_array(); - } - - simdjson_inline simdjson_result 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 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 TAPE_ERROR; - } - } - - 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::simdjson_result(ppc64::ondemand::value_iterator&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} - -} // namespace simdjson - -#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H -/* end file simdjson/generic/ondemand/value_iterator-inl.h for ppc64 */ -/* end file simdjson/generic/ondemand/amalgamated.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 */ @@ -85545,13 +49686,13 @@ namespace simdjson { #undef SIMDJSON_IMPLEMENTATION /* end file simdjson/ppc64/end.h */ -#endif // SIMDJSON_PPC64_ONDEMAND_H -/* end file simdjson/ppc64/ondemand.h */ +#endif // SIMDJSON_PPC64_BUILDER_H +/* end file simdjson/ppc64/builder.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/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 */ @@ -85568,23 +49709,23 @@ namespace simdjson { // 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 { +/** + * Implementation for Westmere (Intel SSE4.2). + */ +namespace westmere { - class implementation; +class implementation; - namespace { - namespace simd { +namespace { +namespace simd { - template struct simd8; - template struct simd8x64; +template struct simd8; +template struct simd8x64; - } // namespace simd - } // unnamed namespace +} // namespace simd +} // unnamed namespace - } // namespace westmere +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_WESTMERE_BASE_H @@ -85639,73 +49780,73 @@ SIMDJSON_TARGET_REGION("sse4.2,pclmul,popcnt") /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { +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) { +// 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; + 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); + 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 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) { +/* 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; + 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); + 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 - } +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); - } +simdjson_inline long long int count_ones(uint64_t input_num) { + return _popcnt64(input_num); +} #endif - simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, - uint64_t* result) { +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { #if SIMDJSON_REGULAR_VISUAL_STUDIO - return _addcarry_u64(0, value1, value2, - reinterpret_cast(result)); + return _addcarry_u64(0, value1, value2, + reinterpret_cast(result)); #else - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); #endif - } +} - } // unnamed namespace - } // namespace westmere +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_WESTMERE_BITMANIPULATION_H @@ -85721,24 +49862,24 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { +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); - } +// +// 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 +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_WESTMERE_BITMASK_H @@ -85759,23 +49900,23 @@ namespace simdjson { // 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 { +/** + * Implementation for Westmere (Intel SSE4.2). + */ +namespace westmere { - class implementation; +class implementation; - namespace { - namespace simd { +namespace { +namespace simd { - template struct simd8; - template struct simd8x64; +template struct simd8; +template struct simd8x64; - } // namespace simd - } // unnamed namespace +} // namespace simd +} // unnamed namespace - } // namespace westmere +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_WESTMERE_BASE_H @@ -85820,49 +49961,49 @@ static_assert(sizeof(__m128i) <= simdjson::SIMDJSON_PADDING, "insufficient paddi /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace numberparsing { +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(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 */ +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(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; +/** @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; + // 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 + 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); + __uint128_t r = (static_cast<__uint128_t>(value1)) * value2; + answer.low = uint64_t(r); + answer.high = uint64_t(r >> 64); #endif - return answer; - } + return answer; +} - } // namespace numberparsing - } // namespace westmere +} // namespace numberparsing +} // namespace westmere } // namespace simdjson #define SIMDJSON_SWAR_NUMBER_PARSING 1 @@ -85881,331 +50022,332 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { - namespace simd { +namespace westmere { +namespace { +namespace simd { - template - struct base { - __m128i value; + template + struct base { + __m128i value; - // Zero constructor - simdjson_inline base() : value{ __m128i() } {} + // Zero constructor + simdjson_inline base() : value{__m128i()} {} - // Conversion from SIMD register - simdjson_inline base(const __m128i _value) : value(_value) {} + // 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; } + // 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(this); *this_cast = *this_cast | other; return *this_cast; } - simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } - simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } - }; + // 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(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; - template> - struct base8 : base> { - typedef uint16_t bitmask_t; - typedef uint32_t bitmask2_t; + template> + struct base8: base> { + typedef uint16_t bitmask_t; + typedef uint32_t bitmask2_t; - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m128i _value) : base>(_value) {} + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m128i _value) : base>(_value) {} - friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm_cmpeq_epi8(lhs, rhs); } + friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm_cmpeq_epi8(lhs, rhs); } - static const int SIZE = sizeof(base>::value); + static const int SIZE = sizeof(base>::value); - template - simdjson_inline simd8 prev(const simd8 prev_chunk) const { - return _mm_alignr_epi8(*this, prev_chunk, 16 - N); - } - }; + template + simdjson_inline simd8 prev(const simd8 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 : base8 { - static simdjson_inline simd8 splat(bool _value) { return _mm_set1_epi8(uint8_t(-(!!_value))); } + // SIMD byte mask type (returned by things like eq and gt) + template<> + struct simd8: base8 { + static simdjson_inline simd8 splat(bool _value) { return _mm_set1_epi8(uint8_t(-(!!_value))); } - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m128i _value) : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) : base8(splat(_value)) {} + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m128i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(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 operator~() const { return *this ^ true; } - }; + 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 operator~() const { return *this ^ true; } + }; - template - struct base8_numeric : base8 { - static simdjson_inline simd8 splat(T _value) { return _mm_set1_epi8(_value); } - static simdjson_inline simd8 zero() { return _mm_setzero_si128(); } - static simdjson_inline simd8 load(const T values[16]) { - return _mm_loadu_si128(reinterpret_cast(values)); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdjson_inline simd8 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + template + struct base8_numeric: base8 { + static simdjson_inline simd8 splat(T _value) { return _mm_set1_epi8(_value); } + static simdjson_inline simd8 zero() { return _mm_setzero_si128(); } + static simdjson_inline simd8 load(const T values[16]) { + return _mm_loadu_si128(reinterpret_cast(values)); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - simdjson_inline base8_numeric() : base8() {} - simdjson_inline base8_numeric(const __m128i _value) : base8(_value) {} + simdjson_inline base8_numeric() : base8() {} + simdjson_inline base8_numeric(const __m128i _value) : base8(_value) {} - // Store to array - simdjson_inline void store(T dst[16]) const { return _mm_storeu_si128(reinterpret_cast<__m128i*>(dst), *this); } + // 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 operator~() const { return *this ^ 0xFFu; } + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - // Addition/subtraction are the same for signed and unsigned - simdjson_inline simd8 operator+(const simd8 other) const { return _mm_add_epi8(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return _mm_sub_epi8(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { return _mm_add_epi8(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return _mm_sub_epi8(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return _mm_shuffle_epi8(lookup_table, *this); - } + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 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 compress(uint32_t mask) would be - // sensible, but the AVX ISA makes this kind of approach difficult. - template - 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(pshufb_combine_table + pop1 * 8)); - __m128i answer = _mm_shuffle_epi8(pruned, compactmask); - _mm_storeu_si128(reinterpret_cast<__m128i*>(output), answer); - } + // 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 compress(uint32_t mask) would be + // sensible, but the AVX ISA makes this kind of approach difficult. + template + 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(pshufb_combine_table + pop1 * 8)); + __m128i answer = _mm_shuffle_epi8(pruned, compactmask); + _mm_storeu_si128(reinterpret_cast<__m128i *>(output), answer); + } - template - simdjson_inline simd8 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::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - }; + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; - // Signed bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + // Signed bytes + template<> + struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - // Order-sensitive comparisons - simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epi8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epi8(*this, other); } - simdjson_inline simd8 operator>(const simd8 other) const { return _mm_cmpgt_epi8(*this, other); } - simdjson_inline simd8 operator<(const simd8 other) const { return _mm_cmpgt_epi8(other, *this); } - }; + // Order-sensitive comparisons + simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epi8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epi8(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return _mm_cmpgt_epi8(*this, other); } + simdjson_inline simd8 operator<(const simd8 other) const { return _mm_cmpgt_epi8(other, *this); } + }; - // Unsigned bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + // Unsigned bytes + template<> + struct simd8: base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - // Saturated math - simdjson_inline simd8 saturating_add(const simd8 other) const { return _mm_adds_epu8(*this, other); } - simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm_subs_epu8(*this, other); } + // Saturated math + simdjson_inline simd8 saturating_add(const simd8 other) const { return _mm_adds_epu8(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm_subs_epu8(*this, other); } - // Order-specific operations - simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epu8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epu8(*this, other); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } - simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } - simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } - simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - simdjson_inline simd8 operator<(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + // Order-specific operations + simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epu8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epu8(*this, other); } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } + simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } + simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } + simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + simdjson_inline simd8 operator<(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - // Bit-specific operations - simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } - simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } - simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } - simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return _mm_testz_si128(*this, bits); } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } - template - simdjson_inline simd8 shr() const { return simd8(_mm_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } - template - simdjson_inline simd8 shl() const { return simd8(_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 - simdjson_inline int get_bit() const { return _mm_movemask_epi8(_mm_slli_epi16(*this, 7 - N)); } - }; + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } + simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } + simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } + simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return _mm_testz_si128(*this, bits); } + simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } + template + simdjson_inline simd8 shr() const { return simd8(_mm_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } + template + simdjson_inline simd8 shl() const { return simd8(_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 + simdjson_inline int get_bit() const { return _mm_movemask_epi8(_mm_slli_epi16(*this, 7-N)); } + }; - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 4, "Westmere kernel should use four registers per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 4, "Westmere kernel should use four registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{ chunk0, chunk1, chunk2, chunk3 } {} - simdjson_inline simd8x64(const T ptr[64]) : chunks{ simd8::load(ptr), simd8::load(ptr + 16), simd8::load(ptr + 32), simd8::load(ptr + 48) } {} + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::load(ptr+16), simd8::load(ptr+32), simd8::load(ptr+48)} {} - simdjson_inline void store(T ptr[64]) const { - this->chunks[0].store(ptr + sizeof(simd8) * 0); - this->chunks[1].store(ptr + sizeof(simd8) * 1); - this->chunks[2].store(ptr + sizeof(simd8) * 2); - this->chunks[3].store(ptr + sizeof(simd8) * 3); - } + simdjson_inline void store(T ptr[64]) const { + this->chunks[0].store(ptr+sizeof(simd8)*0); + this->chunks[1].store(ptr+sizeof(simd8)*1); + this->chunks[2].store(ptr+sizeof(simd8)*2); + this->chunks[3].store(ptr+sizeof(simd8)*3); + } - simdjson_inline simd8 reduce_or() const { - return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]); - } + simdjson_inline simd8 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 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 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] == mask, - this->chunks[1] == mask, - this->chunks[2] == mask, - this->chunks[3] == mask - ).to_bitmask(); - } + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + 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& other) const { - return simd8x64( - 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 eq(const simd8x64 &other) const { + return simd8x64( + 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] <= mask, - this->chunks[1] <= mask, - this->chunks[2] <= mask, - this->chunks[3] <= mask - ).to_bitmask(); - } - }; // struct simd8x64 + simdjson_inline uint64_t lteq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] <= mask, + this->chunks[1] <= mask, + this->chunks[2] <= mask, + this->chunks[3] <= mask + ).to_bitmask(); + } + }; // struct simd8x64 - } // namespace simd - } // unnamed namespace - } // namespace westmere +} // namespace simd +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_WESTMERE_SIMD_INPUT_H @@ -86226,73 +50368,73 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { +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) { +// 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; + 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); + 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 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) { +/* 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; + 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); + 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 - } +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); - } +simdjson_inline long long int count_ones(uint64_t input_num) { + return _popcnt64(input_num); +} #endif - simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, - uint64_t* result) { +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { #if SIMDJSON_REGULAR_VISUAL_STUDIO - return _addcarry_u64(0, value1, value2, - reinterpret_cast(result)); + return _addcarry_u64(0, value1, value2, + reinterpret_cast(result)); #else - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); #endif - } +} - } // unnamed namespace - } // namespace westmere +} // unnamed namespace +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_WESTMERE_BITMANIPULATION_H @@ -86309,374 +50451,78268 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace { - namespace simd { +namespace westmere { +namespace { +namespace simd { - template - struct base { - __m128i value; + template + struct base { + __m128i value; - // Zero constructor - simdjson_inline base() : value{ __m128i() } {} + // Zero constructor + simdjson_inline base() : value{__m128i()} {} - // Conversion from SIMD register - simdjson_inline base(const __m128i _value) : value(_value) {} + // 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; } + // 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(this); *this_cast = *this_cast | other; return *this_cast; } - simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } - simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } - }; + // 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(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; - template> - struct base8 : base> { - typedef uint16_t bitmask_t; - typedef uint32_t bitmask2_t; + template> + struct base8: base> { + typedef uint16_t bitmask_t; + typedef uint32_t bitmask2_t; - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m128i _value) : base>(_value) {} + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m128i _value) : base>(_value) {} - friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm_cmpeq_epi8(lhs, rhs); } + friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm_cmpeq_epi8(lhs, rhs); } - static const int SIZE = sizeof(base>::value); + static const int SIZE = sizeof(base>::value); - template - simdjson_inline simd8 prev(const simd8 prev_chunk) const { - return _mm_alignr_epi8(*this, prev_chunk, 16 - N); - } - }; + template + simdjson_inline simd8 prev(const simd8 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 : base8 { - static simdjson_inline simd8 splat(bool _value) { return _mm_set1_epi8(uint8_t(-(!!_value))); } + // SIMD byte mask type (returned by things like eq and gt) + template<> + struct simd8: base8 { + static simdjson_inline simd8 splat(bool _value) { return _mm_set1_epi8(uint8_t(-(!!_value))); } - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m128i _value) : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) : base8(splat(_value)) {} + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m128i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(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 operator~() const { return *this ^ true; } - }; + 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 operator~() const { return *this ^ true; } + }; - template - struct base8_numeric : base8 { - static simdjson_inline simd8 splat(T _value) { return _mm_set1_epi8(_value); } - static simdjson_inline simd8 zero() { return _mm_setzero_si128(); } - static simdjson_inline simd8 load(const T values[16]) { - return _mm_loadu_si128(reinterpret_cast(values)); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdjson_inline simd8 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + template + struct base8_numeric: base8 { + static simdjson_inline simd8 splat(T _value) { return _mm_set1_epi8(_value); } + static simdjson_inline simd8 zero() { return _mm_setzero_si128(); } + static simdjson_inline simd8 load(const T values[16]) { + return _mm_loadu_si128(reinterpret_cast(values)); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - simdjson_inline base8_numeric() : base8() {} - simdjson_inline base8_numeric(const __m128i _value) : base8(_value) {} + simdjson_inline base8_numeric() : base8() {} + simdjson_inline base8_numeric(const __m128i _value) : base8(_value) {} - // Store to array - simdjson_inline void store(T dst[16]) const { return _mm_storeu_si128(reinterpret_cast<__m128i*>(dst), *this); } + // 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 operator~() const { return *this ^ 0xFFu; } + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - // Addition/subtraction are the same for signed and unsigned - simdjson_inline simd8 operator+(const simd8 other) const { return _mm_add_epi8(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return _mm_sub_epi8(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { return _mm_add_epi8(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return _mm_sub_epi8(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return _mm_shuffle_epi8(lookup_table, *this); - } + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 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 compress(uint32_t mask) would be - // sensible, but the AVX ISA makes this kind of approach difficult. - template - 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(pshufb_combine_table + pop1 * 8)); - __m128i answer = _mm_shuffle_epi8(pruned, compactmask); - _mm_storeu_si128(reinterpret_cast<__m128i*>(output), answer); - } + // 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 compress(uint32_t mask) would be + // sensible, but the AVX ISA makes this kind of approach difficult. + template + 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(pshufb_combine_table + pop1 * 8)); + __m128i answer = _mm_shuffle_epi8(pruned, compactmask); + _mm_storeu_si128(reinterpret_cast<__m128i *>(output), answer); + } - template - simdjson_inline simd8 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::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - }; + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; - // Signed bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + // Signed bytes + template<> + struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - // Order-sensitive comparisons - simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epi8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epi8(*this, other); } - simdjson_inline simd8 operator>(const simd8 other) const { return _mm_cmpgt_epi8(*this, other); } - simdjson_inline simd8 operator<(const simd8 other) const { return _mm_cmpgt_epi8(other, *this); } - }; + // Order-sensitive comparisons + simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epi8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epi8(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return _mm_cmpgt_epi8(*this, other); } + simdjson_inline simd8 operator<(const simd8 other) const { return _mm_cmpgt_epi8(other, *this); } + }; - // Unsigned bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + // Unsigned bytes + template<> + struct simd8: base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - // Saturated math - simdjson_inline simd8 saturating_add(const simd8 other) const { return _mm_adds_epu8(*this, other); } - simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm_subs_epu8(*this, other); } + // Saturated math + simdjson_inline simd8 saturating_add(const simd8 other) const { return _mm_adds_epu8(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm_subs_epu8(*this, other); } - // Order-specific operations - simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epu8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epu8(*this, other); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } - simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } - simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } - simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - simdjson_inline simd8 operator<(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + // Order-specific operations + simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epu8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epu8(*this, other); } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } + simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } + simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } + simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + simdjson_inline simd8 operator<(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - // Bit-specific operations - simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } - simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } - simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } - simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return _mm_testz_si128(*this, bits); } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } - template - simdjson_inline simd8 shr() const { return simd8(_mm_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } - template - simdjson_inline simd8 shl() const { return simd8(_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 - simdjson_inline int get_bit() const { return _mm_movemask_epi8(_mm_slli_epi16(*this, 7 - N)); } - }; + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } + simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } + simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } + simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return _mm_testz_si128(*this, bits); } + simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } + template + simdjson_inline simd8 shr() const { return simd8(_mm_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } + template + simdjson_inline simd8 shl() const { return simd8(_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 + simdjson_inline int get_bit() const { return _mm_movemask_epi8(_mm_slli_epi16(*this, 7-N)); } + }; - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 4, "Westmere kernel should use four registers per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 4, "Westmere kernel should use four registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{ chunk0, chunk1, chunk2, chunk3 } {} - simdjson_inline simd8x64(const T ptr[64]) : chunks{ simd8::load(ptr), simd8::load(ptr + 16), simd8::load(ptr + 32), simd8::load(ptr + 48) } {} + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::load(ptr+16), simd8::load(ptr+32), simd8::load(ptr+48)} {} - simdjson_inline void store(T ptr[64]) const { - this->chunks[0].store(ptr + sizeof(simd8) * 0); - this->chunks[1].store(ptr + sizeof(simd8) * 1); - this->chunks[2].store(ptr + sizeof(simd8) * 2); - this->chunks[3].store(ptr + sizeof(simd8) * 3); - } + simdjson_inline void store(T ptr[64]) const { + this->chunks[0].store(ptr+sizeof(simd8)*0); + this->chunks[1].store(ptr+sizeof(simd8)*1); + this->chunks[2].store(ptr+sizeof(simd8)*2); + this->chunks[3].store(ptr+sizeof(simd8)*3); + } - simdjson_inline simd8 reduce_or() const { - return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]); - } + simdjson_inline simd8 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 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 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] == mask, - this->chunks[1] == mask, - this->chunks[2] == mask, - this->chunks[3] == mask - ).to_bitmask(); - } + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + 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& other) const { - return simd8x64( - 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 eq(const simd8x64 &other) const { + return simd8x64( + 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] <= mask, - this->chunks[1] <= mask, - this->chunks[2] <= mask, - this->chunks[3] <= mask - ).to_bitmask(); - } - }; // struct simd8x64 + simdjson_inline uint64_t lteq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] <= mask, + this->chunks[1] <= mask, + this->chunks[2] <= mask, + this->chunks[3] <= mask + ).to_bitmask(); + } + }; // struct simd8x64 - } // namespace simd - } // unnamed namespace - } // namespace westmere +} // 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 { +namespace westmere { +namespace { - using namespace simd; +using namespace simd; - // Holds backslashes and quotes locations. - struct backslash_and_quote { - public: - static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t* src, uint8_t* dst); +// 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); } + 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 + 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 v0(src); - simd8 v1(src + 16); - v0.store(dst); - v1.store(dst + 16); - uint64_t bs_and_quote = simd8x64(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); - return { - uint32_t(bs_and_quote), // bs_bits - uint32_t(bs_and_quote >> 32) // quote_bits - }; +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 v0(src); + simd8 v1(src + 16); + v0.store(dst); + v1.store(dst + 16); + uint64_t bs_and_quote = simd8x64(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 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 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 +struct has_custom_serialization : std::false_type {}; + +inline constexpr struct serialize_tag { + template + constexpr void operator()(westmere::builder::string_builder& b, T&& obj) const{ + return tag_invoke(*this, b, std::forward(obj)); + } + + +} serialize{}; +template +struct has_custom_serialization(), std::declval())) +>> : std::true_type {}; + +template +constexpr bool require_custom_serialization = has_custom_serialization::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::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 + 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 + simdjson_inline void append_key_value(key_type key, value_type value) noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + template + simdjson_inline void append_key_value(value_type value) noexcept; + + // Support for optional types (std::optional, etc.) + template + requires(!require_custom_serialization) + simdjson_inline void append(const T &opt); + + template + requires(require_custom_serialization) + simdjson_inline void append(T &&val); + + // Support for string-like types + template + requires(std::is_convertible::value || + std::is_same::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 +requires (!std::is_convertible::value && !require_custom_serialization) + 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; +#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 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 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; + +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); + + /** + * 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. + */ + simdjson_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 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 +simdjson_warn_unused simdjson_result 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 +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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ +#if SIMDJSON_STATIC_REFLECTION + +#include +#include +#include +#include +#include +#include +#include +#include +// #include // for std::define_static_string - header not available yet + +namespace simdjson { +namespace westmere { +namespace builder { + +template + requires(concepts::container_but_not_string && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + auto it = t.begin(); + auto end = t.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +constexpr void atom(string_builder &b, const T &t) { + b.escape_and_append_with_quotes(t); +} + +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &m) { + if (m.empty()) { + b.append_raw("{}"); + return; + } + b.append('{'); + bool first = true; + for (const auto& [key, value] : m) { + if (!first) { + b.append(','); + } + first = false; + // Keys must be convertible to string_view per the concept + b.escape_and_append_with_quotes(key); + b.append(':'); + atom(b, value); + } + b.append('}'); +} + + +template::value && !std::is_same_v>::type> +constexpr void atom(string_builder &b, const number_type t) { + b.append(t); +} + +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, t.[:dm:]); + i++; + }; + b.append('}'); +} + +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &opt) { + if (opt) { + atom(b, opt.value()); + } else { + b.append_raw("null"); + } +} + +// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &ptr) { + if (ptr) { + atom(b, *ptr); + } else { + b.append_raw("null"); + } +} + +// Support for enums - serialize as string representation using expand approach from P2996R12 +template + requires(std::is_enum_v && !require_custom_serialization) +void atom(string_builder &b, const T &e) { +#if SIMDJSON_STATIC_REFLECTION + 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(std::meta::identifier_of(enum_val))); + if (e == [:enum_val:]) { + b.append_raw(enum_str); + return; + } + }; + // Fallback to integer if enum value not found + atom(b, static_cast>(e)); +#else + // Fallback: serialize as integer if reflection not available + atom(b, static_cast>(e)); +#endif +} + +// Support for appendable containers that don't have operator[] (sets, etc.) +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &container) { + if (container.empty()) { + b.append_raw("[]"); + return; + } + b.append('['); + bool first = true; + for (const auto& item : container) { + if (!first) { + b.append(','); + } + first = false; + atom(b, item); + } + b.append(']'); +} + +// append functions that delegate to atom functions for primitive types +template + requires(std::is_arithmetic_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +// works for struct +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^Z, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, z.[:dm:]); + i++; + }; + b.append('}'); +} + +// works for container that have begin() and end() iterators +template + requires(concepts::container_but_not_string && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + auto it = z.begin(); + auto end = z.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} + +template + requires (require_custom_serialization) +void append(string_builder &b, const Z &z) { + b.append(z); +} + + +template +simdjson_warn_unused simdjson_result to_json_string(const Z &z, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +template +simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + 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 +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 + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +void extract_from(string_builder &b, const T &obj) { + // 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) || ...); + }; + + b.append('{'); + bool first = true; + + // 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_public(mem)) { + 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 (should_extract(key)) { + if (!first) { + b.append(','); + } + first = false; + + // Serialize the key + constexpr auto quoted_key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem))); + b.append_raw(quoted_key); + b.append(':'); + + // Serialize the value + atom(b, obj.[:mem:]); + } + } + }; + + b.append('}'); +} + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + extract_from(b, obj); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +} // namespace builder +} // namespace westmere +// Alias the function template to 'to' in the global namespace +template +simdjson_warn_unused simdjson_result to_json(const Z &z, size_t initial_capacity = westmere::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + westmere::builder::string_builder b(initial_capacity); + westmere::builder::append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} +template +simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = westmere::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + westmere::builder::string_builder b(initial_capacity); + westmere::builder::append(b, z); + std::string_view view; + if(auto e = b.view().get(view); e) { return e; } + s.assign(view); + return SUCCESS; +} +// Global namespace function for extract_from +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result 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(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 +simdjson_warn_unused simdjson_result 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 + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result 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(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 +simdjson_warn_unused simdjson_result 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 + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result 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(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 +#include +#include +#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 SIMDJSON_EXPERIMENTAL_HAS_NEON +#include +#ifdef _MSC_VER +#include +#endif +#endif +#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 +#include +#ifdef _MSC_VER +#include +#endif +#endif + +namespace simdjson { +namespace westmere { +namespace builder { + +static SIMDJSON_CONSTEXPR_LAMBDA std::array + 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; +} + +**/ + +SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline bool +simple_needs_escaping(std::string_view v) { + for (char c : v) { + // a table lookup is faster than a series of comparisons + if (json_quotable_character[static_cast(c)]) { + return true; + } + } + return false; +} + +#if SIMDJSON_EXPERIMENTAL_HAS_NEON +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + uint8x16_t running = vdupq_n_u8(0); + uint8x16_t v34 = vdupq_n_u8(34); + uint8x16_t v92 = vdupq_n_u8(92); + + for (; i + 15 < view.size(); i += 16) { + uint8x16_t word = vld1q_u8((const uint8_t *)view.data() + i); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + if (i < view.size()) { + uint8x16_t word = + vld1q_u8((const uint8_t *)view.data() + view.length() - 16); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + return vmaxvq_u32(vreinterpretq_u32_u8(running)) != 0; +} +#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2 +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + __m128i running = _mm_setzero_si128(); + for (; i + 15 < view.size(); i += 16) { + + __m128i word = + _mm_loadu_si128(reinterpret_cast(view.data() + i)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + if (i < view.size()) { + __m128i word = _mm_loadu_si128( + reinterpret_cast(view.data() + view.length() - 16)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + return _mm_movemask_epi8(running) != 0; +} +#else +simdjson_inline bool fast_needs_escaping(std::string_view view) { + return simple_needs_escaping(view); +} +#endif + +// 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(*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(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)); + + if (vmaxvq_u32(vreinterpretq_u32_u8(needs_escape)) != 0) { + // Found quotable character - extract exact byte position using ctz + uint64x2_t as64 = vreinterpretq_u64_u8(needs_escape); + uint64_t lo = vgetq_lane_u64(as64, 0); + uint64_t hi = vgetq_lane_u64(as64, 1); + size_t offset = ptr - reinterpret_cast(view.data()); +#ifdef _MSC_VER + unsigned long trailing_zero = 0; + if (lo != 0) { + _BitScanForward64(&trailing_zero, lo); + return offset + trailing_zero / 8; + } else { + _BitScanForward64(&trailing_zero, hi); + return offset + 8 + trailing_zero / 8; + } +#else + if (lo != 0) { + return offset + __builtin_ctzll(lo) / 8; + } else { + return offset + 8 + __builtin_ctzll(hi) / 8; + } +#endif + } + 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(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(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(view.data()); +#ifdef _MSC_VER + unsigned long trailing_zero = 0; + _BitScanForward(&trailing_zero, mask); + return offset + trailing_zero; +#else + return offset + __builtin_ctz(mask); +#endif + } + 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(); + } +} + +// 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 out - initout; +} + +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; +} + +simdjson_inline void string_builder::grow_buffer(size_t desired_capacity) { + if (!is_valid) { + return; + } + std::unique_ptr 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 { + +template ::value>::type> +simdjson_really_inline int int_log2(number_type x) { + return 63 - leading_zeroes(uint64_t(x) | 1); +} + +simdjson_really_inline int fast_digit_count_32(uint32_t x) { + static uint64_t table[] = { + 4294967296, 8589934582, 8589934582, 8589934582, 12884901788, + 12884901788, 12884901788, 17179868184, 17179868184, 17179868184, + 21474826480, 21474826480, 21474826480, 21474826480, 25769703776, + 25769703776, 25769703776, 30063771072, 30063771072, 30063771072, + 34349738368, 34349738368, 34349738368, 34349738368, 38554705664, + 38554705664, 38554705664, 41949672960, 41949672960, 41949672960, + 42949672960, 42949672960}; + return uint32_t((x + table[int_log2(x)]) >> 32); +} + +simdjson_really_inline int fast_digit_count_64(uint64_t x) { + static uint64_t table[] = {9, + 99, + 999, + 9999, + 99999, + 999999, + 9999999, + 99999999, + 999999999, + 9999999999, + 99999999999, + 999999999999, + 9999999999999, + 99999999999999, + 999999999999999ULL, + 9999999999999999ULL, + 99999999999999999ULL, + 999999999999999999ULL, + 9999999999999999999ULL}; + int y = (19 * int_log2(x) >> 6); + y += x > table[y]; + return y + 1; +} + +template ::value>::type> +simdjson_really_inline size_t digit_count(number_type v) noexcept { + static_assert(sizeof(number_type) == 8 || sizeof(number_type) == 4 || + sizeof(number_type) == 2 || sizeof(number_type) == 1, + "We only support 8-bit, 16-bit, 32-bit and 64-bit numbers"); + SIMDJSON_IF_CONSTEXPR(sizeof(number_type) <= 4) { + return fast_digit_count_32(static_cast(v)); + } + else { + return fast_digit_count_64(static_cast(v)); + } +} +static const char decimal_table[200] = { + 0x30, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35, + 0x30, 0x36, 0x30, 0x37, 0x30, 0x38, 0x30, 0x39, 0x31, 0x30, 0x31, 0x31, + 0x31, 0x32, 0x31, 0x33, 0x31, 0x34, 0x31, 0x35, 0x31, 0x36, 0x31, 0x37, + 0x31, 0x38, 0x31, 0x39, 0x32, 0x30, 0x32, 0x31, 0x32, 0x32, 0x32, 0x33, + 0x32, 0x34, 0x32, 0x35, 0x32, 0x36, 0x32, 0x37, 0x32, 0x38, 0x32, 0x39, + 0x33, 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x33, 0x34, 0x33, 0x35, + 0x33, 0x36, 0x33, 0x37, 0x33, 0x38, 0x33, 0x39, 0x34, 0x30, 0x34, 0x31, + 0x34, 0x32, 0x34, 0x33, 0x34, 0x34, 0x34, 0x35, 0x34, 0x36, 0x34, 0x37, + 0x34, 0x38, 0x34, 0x39, 0x35, 0x30, 0x35, 0x31, 0x35, 0x32, 0x35, 0x33, + 0x35, 0x34, 0x35, 0x35, 0x35, 0x36, 0x35, 0x37, 0x35, 0x38, 0x35, 0x39, + 0x36, 0x30, 0x36, 0x31, 0x36, 0x32, 0x36, 0x33, 0x36, 0x34, 0x36, 0x35, + 0x36, 0x36, 0x36, 0x37, 0x36, 0x38, 0x36, 0x39, 0x37, 0x30, 0x37, 0x31, + 0x37, 0x32, 0x37, 0x33, 0x37, 0x34, 0x37, 0x35, 0x37, 0x36, 0x37, 0x37, + 0x37, 0x38, 0x37, 0x39, 0x38, 0x30, 0x38, 0x31, 0x38, 0x32, 0x38, 0x33, + 0x38, 0x34, 0x38, 0x35, 0x38, 0x36, 0x38, 0x37, 0x38, 0x38, 0x38, 0x39, + 0x39, 0x30, 0x39, 0x31, 0x39, 0x32, 0x39, 0x33, 0x39, 0x34, 0x39, 0x35, + 0x39, 0x36, 0x39, 0x37, 0x39, 0x38, 0x39, 0x39, +}; +} // namespace internal + +template +simdjson_inline void string_builder::append(number_type v) noexcept { + static_assert(std::is_same::value || + std::is_integral::value || + std::is_floating_point::value, + "Unsupported number type"); + // If C++17 is available, we can 'if constexpr' here. + SIMDJSON_IF_CONSTEXPR(std::is_same::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::value) { + // Process 4 digits at a time instead of 2, reducing store operations + // and divisions by approximately half for large numbers. + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + unsigned_type pv = static_cast(v); + size_t dc = internal::digit_count(pv); + char *write_pointer = buffer.get() + position + dc - 1; + + // Process 4 digits per iteration for large numbers + while (pv >= 10000) { + unsigned_type q = pv / 10000; + unsigned_type r = pv % 10000; + unsigned_type r_hi = r / 100; // High 2 digits of remainder + unsigned_type r_lo = r % 100; // Low 2 digits of remainder + // Write low 2 digits first (rightmost), then high 2 digits + memcpy(write_pointer - 1, &internal::decimal_table[r_lo * 2], 2); + memcpy(write_pointer - 3, &internal::decimal_table[r_hi * 2], 2); + write_pointer -= 4; + pv = q; + } + + // Handle remaining 1-4 digits with original 2-digit loop + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_integral::value) { + // Same 4-digit batching as unsigned path for signed integers + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + bool negative = v < 0; + unsigned_type pv = static_cast(v); + if (negative) { + pv = 0 - pv; // the 0 is for Microsoft + } + size_t dc = internal::digit_count(pv); + // by always writing the minus sign, we avoid the branch. + buffer.get()[position] = '-'; + position += negative ? 1 : 0; + char *write_pointer = buffer.get() + position + dc - 1; + + // Process 4 digits per iteration for large numbers + while (pv >= 10000) { + unsigned_type q = pv / 10000; + unsigned_type r = pv % 10000; + unsigned_type r_hi = r / 100; + unsigned_type r_lo = r % 100; + memcpy(write_pointer - 1, &internal::decimal_table[r_lo * 2], 2); + memcpy(write_pointer - 3, &internal::decimal_table[r_hi * 2], 2); + write_pointer -= 4; + pv = q; + } + + // Handle remaining 1-4 digits + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_floating_point::value) { + constexpr size_t max_number_size = 24; + if (capacity_check(max_number_size)) { + // 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. + 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. + 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 +simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept { + escape_and_append_with_quotes(constevalutil::string_constant::value); +} +#endif + +simdjson_inline void string_builder::append_raw(const char *c) noexcept { + size_t len = std::strlen(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; + } +} +#if SIMDJSON_SUPPORTS_CONCEPTS +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +simdjson_inline void string_builder::append(const T &opt) { + if (opt) { + append(*opt); + } else { + append_null(); + } +} + +template + requires(require_custom_serialization) +simdjson_inline void string_builder::append(T &&val) { + serialize(*this, std::forward(val)); +} + +template + requires(std::is_convertible::value || + std::is_same::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 + requires(!std::is_convertible::value && !require_custom_serialization) +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>) { + 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 +string_builder::view() const noexcept { + if (!is_valid) { + return simdjson::OUT_OF_CAPACITY; + } + return std::string_view(buffer.get(), position); +} + +simdjson_inline simdjson_result 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 +simdjson_inline void +string_builder::append_key_value(key_type key, value_type value) noexcept { + static_assert(std::is_same::value || + std::is_convertible::value, + "Unsupported key type"); + escape_and_append_with_quotes(key); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(value); + } +} + +#if SIMDJSON_SUPPORTS_CONCEPTS +template +simdjson_inline void +string_builder::append_key_value(value_type value) noexcept { + escape_and_append_with_quotes(); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(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 struct simd8; +template 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 + +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); +} + +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t *result) { + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); +} + +} // 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 + +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 + 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(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; + + // Forward-declared so they can be used by splat and friends. + template + struct simd8; + + template> + struct base8: base> { + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m128i _value) : base>(_value) {} + + friend simdjson_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return __lsx_vseq_b(lhs, rhs); } + + static const int SIZE = sizeof(base>::value); + + template + simdjson_inline simd8 prev(const simd8 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: base8 { + static simdjson_inline simd8 splat(bool _value) { + return __lsx_vreplgr2vr_b(uint8_t(-(!!_value))); + } + + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m128i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(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 operator~() const { return *this ^ true; } + }; + + template + struct base8_numeric: base8 { + static simdjson_inline simd8 splat(T _value) { return __lsx_vreplgr2vr_b(_value); } + static simdjson_inline simd8 zero() { return __lsx_vldi(0); } + static simdjson_inline simd8 load(const T values[16]) { + return __lsx_vld(reinterpret_cast(values), 0); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } + + simdjson_inline base8_numeric() : base8() {} + simdjson_inline base8_numeric(const __m128i _value) : base8(_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 operator+(const simd8 other) const { return __lsx_vadd_b(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return __lsx_vsub_b(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } + + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } + + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 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 + 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(reinterpret_cast(pshufb_combine_table)), pop1 * 8); + __m128i answer = __lsx_vshuf_b(pruned, pruned, compactmask); + __lsx_vst(answer, reinterpret_cast(output), 0); + } + + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; + + // Signed bytes + template<> + struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } + + // Order-sensitive comparisons + simdjson_inline simd8 max_val(const simd8 other) const { return __lsx_vmax_b(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return __lsx_vmin_b(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return __lsx_vslt_b(other, *this); } + simdjson_inline simd8 operator<(const simd8 other) const { return __lsx_vslt_b(*this, other); } + }; + + // Unsigned bytes + template<> + struct simd8: base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } + + // Saturated math + simdjson_inline simd8 saturating_add(const simd8 other) const { return __lsx_vsadd_bu(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return __lsx_vssub_bu(*this, other); } + + // Order-specific operations + simdjson_inline simd8 max_val(const simd8 other) const { return __lsx_vmax_bu(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return __lsx_vmin_bu(other, *this); } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } + simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } + simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } + simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } + + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } + simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } + simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } + simdjson_inline simd8 any_bits_set(simd8 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 bits) const { + return 0 == __lsx_vpickve2gr_hu(__lsx_vmsknz_b(__lsx_vand_v(*this, bits)), 0); + } + simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } + template + simdjson_inline simd8 shr() const { return simd8(__lsx_vsrli_b(*this, N)); } + template + simdjson_inline simd8 shl() const { return simd8(__lsx_vslli_b(*this, N)); } + }; + + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 4, "LSX kernel should use four registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } + + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed + + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::load(ptr+16), simd8::load(ptr+32), simd8::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(output); + // There should be a critical value which processes in scaler is faster. + if (zcnt1) + this->chunks[0].compress(mask1, reinterpret_cast(voutput)); + voutput += zcnt1; + if (zcnt2) + this->chunks[1].compress(mask2, reinterpret_cast(voutput)); + voutput += zcnt2; + if (zcnt3) + this->chunks[2].compress(mask3, reinterpret_cast(voutput)); + voutput += zcnt3; + if (zcnt4) + this->chunks[3].compress(mask4, reinterpret_cast(voutput)); + voutput += zcnt4; + return reinterpret_cast(voutput) - reinterpret_cast(output); + } + + simdjson_inline void store(T ptr[64]) const { + this->chunks[0].store(ptr+sizeof(simd8)*0); + this->chunks[1].store(ptr+sizeof(simd8)*1); + this->chunks[2].store(ptr+sizeof(simd8)*2); + this->chunks[3].store(ptr+sizeof(simd8)*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 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 mask = simd8::splat(m); + return simd8x64( + 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 &other) const { + return simd8x64( + 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 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] <= mask, + this->chunks[1] <= mask, + this->chunks[2] <= mask, + this->chunks[3] <= mask + ).to_bitmask(); + } + }; // struct simd8x64 + +} // 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 v0(src); + simd8 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(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 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + static_cast((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 +struct has_custom_serialization : std::false_type {}; + +inline constexpr struct serialize_tag { + template + constexpr void operator()(lsx::builder::string_builder& b, T&& obj) const{ + return tag_invoke(*this, b, std::forward(obj)); + } + + +} serialize{}; +template +struct has_custom_serialization(), std::declval())) +>> : std::true_type {}; + +template +constexpr bool require_custom_serialization = has_custom_serialization::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::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 + 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 + simdjson_inline void append_key_value(key_type key, value_type value) noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + template + simdjson_inline void append_key_value(value_type value) noexcept; + + // Support for optional types (std::optional, etc.) + template + requires(!require_custom_serialization) + simdjson_inline void append(const T &opt); + + template + requires(require_custom_serialization) + simdjson_inline void append(T &&val); + + // Support for string-like types + template + requires(std::is_convertible::value || + std::is_same::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 +requires (!std::is_convertible::value && !require_custom_serialization) + 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; +#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 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 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; + +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); + + /** + * 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. + */ + simdjson_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 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 +simdjson_warn_unused simdjson_result 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 +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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ +#if SIMDJSON_STATIC_REFLECTION + +#include +#include +#include +#include +#include +#include +#include +#include +// #include // for std::define_static_string - header not available yet + +namespace simdjson { +namespace lsx { +namespace builder { + +template + requires(concepts::container_but_not_string && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + auto it = t.begin(); + auto end = t.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +constexpr void atom(string_builder &b, const T &t) { + b.escape_and_append_with_quotes(t); +} + +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &m) { + if (m.empty()) { + b.append_raw("{}"); + return; + } + b.append('{'); + bool first = true; + for (const auto& [key, value] : m) { + if (!first) { + b.append(','); + } + first = false; + // Keys must be convertible to string_view per the concept + b.escape_and_append_with_quotes(key); + b.append(':'); + atom(b, value); + } + b.append('}'); +} + + +template::value && !std::is_same_v>::type> +constexpr void atom(string_builder &b, const number_type t) { + b.append(t); +} + +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, t.[:dm:]); + i++; + }; + b.append('}'); +} + +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &opt) { + if (opt) { + atom(b, opt.value()); + } else { + b.append_raw("null"); + } +} + +// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &ptr) { + if (ptr) { + atom(b, *ptr); + } else { + b.append_raw("null"); + } +} + +// Support for enums - serialize as string representation using expand approach from P2996R12 +template + requires(std::is_enum_v && !require_custom_serialization) +void atom(string_builder &b, const T &e) { +#if SIMDJSON_STATIC_REFLECTION + 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(std::meta::identifier_of(enum_val))); + if (e == [:enum_val:]) { + b.append_raw(enum_str); + return; + } + }; + // Fallback to integer if enum value not found + atom(b, static_cast>(e)); +#else + // Fallback: serialize as integer if reflection not available + atom(b, static_cast>(e)); +#endif +} + +// Support for appendable containers that don't have operator[] (sets, etc.) +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &container) { + if (container.empty()) { + b.append_raw("[]"); + return; + } + b.append('['); + bool first = true; + for (const auto& item : container) { + if (!first) { + b.append(','); + } + first = false; + atom(b, item); + } + b.append(']'); +} + +// append functions that delegate to atom functions for primitive types +template + requires(std::is_arithmetic_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +// works for struct +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^Z, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, z.[:dm:]); + i++; + }; + b.append('}'); +} + +// works for container that have begin() and end() iterators +template + requires(concepts::container_but_not_string && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + auto it = z.begin(); + auto end = z.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} + +template + requires (require_custom_serialization) +void append(string_builder &b, const Z &z) { + b.append(z); +} + + +template +simdjson_warn_unused simdjson_result to_json_string(const Z &z, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +template +simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + 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 +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 + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +void extract_from(string_builder &b, const T &obj) { + // 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) || ...); + }; + + b.append('{'); + bool first = true; + + // 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_public(mem)) { + 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 (should_extract(key)) { + if (!first) { + b.append(','); + } + first = false; + + // Serialize the key + constexpr auto quoted_key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem))); + b.append_raw(quoted_key); + b.append(':'); + + // Serialize the value + atom(b, obj.[:mem:]); + } + } + }; + + b.append('}'); +} + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + extract_from(b, obj); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +} // namespace builder +} // namespace lsx +// Alias the function template to 'to' in the global namespace +template +simdjson_warn_unused simdjson_result to_json(const Z &z, size_t initial_capacity = lsx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + lsx::builder::string_builder b(initial_capacity); + lsx::builder::append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} +template +simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = lsx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + lsx::builder::string_builder b(initial_capacity); + lsx::builder::append(b, z); + std::string_view view; + if(auto e = b.view().get(view); e) { return e; } + s.assign(view); + return SUCCESS; +} +// Global namespace function for extract_from +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result 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(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 +simdjson_warn_unused simdjson_result 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 + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result 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(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 +simdjson_warn_unused simdjson_result 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 + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result 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(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 +#include +#include +#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 SIMDJSON_EXPERIMENTAL_HAS_NEON +#include +#ifdef _MSC_VER +#include +#endif +#endif +#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 +#include +#ifdef _MSC_VER +#include +#endif +#endif + +namespace simdjson { +namespace lsx { +namespace builder { + +static SIMDJSON_CONSTEXPR_LAMBDA std::array + 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; +} + +**/ + +SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline bool +simple_needs_escaping(std::string_view v) { + for (char c : v) { + // a table lookup is faster than a series of comparisons + if (json_quotable_character[static_cast(c)]) { + return true; + } + } + return false; +} + +#if SIMDJSON_EXPERIMENTAL_HAS_NEON +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + uint8x16_t running = vdupq_n_u8(0); + uint8x16_t v34 = vdupq_n_u8(34); + uint8x16_t v92 = vdupq_n_u8(92); + + for (; i + 15 < view.size(); i += 16) { + uint8x16_t word = vld1q_u8((const uint8_t *)view.data() + i); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + if (i < view.size()) { + uint8x16_t word = + vld1q_u8((const uint8_t *)view.data() + view.length() - 16); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + return vmaxvq_u32(vreinterpretq_u32_u8(running)) != 0; +} +#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2 +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + __m128i running = _mm_setzero_si128(); + for (; i + 15 < view.size(); i += 16) { + + __m128i word = + _mm_loadu_si128(reinterpret_cast(view.data() + i)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + if (i < view.size()) { + __m128i word = _mm_loadu_si128( + reinterpret_cast(view.data() + view.length() - 16)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + return _mm_movemask_epi8(running) != 0; +} +#else +simdjson_inline bool fast_needs_escaping(std::string_view view) { + return simple_needs_escaping(view); +} +#endif + +// 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(*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(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)); + + if (vmaxvq_u32(vreinterpretq_u32_u8(needs_escape)) != 0) { + // Found quotable character - extract exact byte position using ctz + uint64x2_t as64 = vreinterpretq_u64_u8(needs_escape); + uint64_t lo = vgetq_lane_u64(as64, 0); + uint64_t hi = vgetq_lane_u64(as64, 1); + size_t offset = ptr - reinterpret_cast(view.data()); +#ifdef _MSC_VER + unsigned long trailing_zero = 0; + if (lo != 0) { + _BitScanForward64(&trailing_zero, lo); + return offset + trailing_zero / 8; + } else { + _BitScanForward64(&trailing_zero, hi); + return offset + 8 + trailing_zero / 8; + } +#else + if (lo != 0) { + return offset + __builtin_ctzll(lo) / 8; + } else { + return offset + 8 + __builtin_ctzll(hi) / 8; + } +#endif + } + 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(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(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(view.data()); +#ifdef _MSC_VER + unsigned long trailing_zero = 0; + _BitScanForward(&trailing_zero, mask); + return offset + trailing_zero; +#else + return offset + __builtin_ctz(mask); +#endif + } + 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(); + } +} + +// 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 out - initout; +} + +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; +} + +simdjson_inline void string_builder::grow_buffer(size_t desired_capacity) { + if (!is_valid) { + return; + } + std::unique_ptr 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 { + +template ::value>::type> +simdjson_really_inline int int_log2(number_type x) { + return 63 - leading_zeroes(uint64_t(x) | 1); +} + +simdjson_really_inline int fast_digit_count_32(uint32_t x) { + static uint64_t table[] = { + 4294967296, 8589934582, 8589934582, 8589934582, 12884901788, + 12884901788, 12884901788, 17179868184, 17179868184, 17179868184, + 21474826480, 21474826480, 21474826480, 21474826480, 25769703776, + 25769703776, 25769703776, 30063771072, 30063771072, 30063771072, + 34349738368, 34349738368, 34349738368, 34349738368, 38554705664, + 38554705664, 38554705664, 41949672960, 41949672960, 41949672960, + 42949672960, 42949672960}; + return uint32_t((x + table[int_log2(x)]) >> 32); +} + +simdjson_really_inline int fast_digit_count_64(uint64_t x) { + static uint64_t table[] = {9, + 99, + 999, + 9999, + 99999, + 999999, + 9999999, + 99999999, + 999999999, + 9999999999, + 99999999999, + 999999999999, + 9999999999999, + 99999999999999, + 999999999999999ULL, + 9999999999999999ULL, + 99999999999999999ULL, + 999999999999999999ULL, + 9999999999999999999ULL}; + int y = (19 * int_log2(x) >> 6); + y += x > table[y]; + return y + 1; +} + +template ::value>::type> +simdjson_really_inline size_t digit_count(number_type v) noexcept { + static_assert(sizeof(number_type) == 8 || sizeof(number_type) == 4 || + sizeof(number_type) == 2 || sizeof(number_type) == 1, + "We only support 8-bit, 16-bit, 32-bit and 64-bit numbers"); + SIMDJSON_IF_CONSTEXPR(sizeof(number_type) <= 4) { + return fast_digit_count_32(static_cast(v)); + } + else { + return fast_digit_count_64(static_cast(v)); + } +} +static const char decimal_table[200] = { + 0x30, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35, + 0x30, 0x36, 0x30, 0x37, 0x30, 0x38, 0x30, 0x39, 0x31, 0x30, 0x31, 0x31, + 0x31, 0x32, 0x31, 0x33, 0x31, 0x34, 0x31, 0x35, 0x31, 0x36, 0x31, 0x37, + 0x31, 0x38, 0x31, 0x39, 0x32, 0x30, 0x32, 0x31, 0x32, 0x32, 0x32, 0x33, + 0x32, 0x34, 0x32, 0x35, 0x32, 0x36, 0x32, 0x37, 0x32, 0x38, 0x32, 0x39, + 0x33, 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x33, 0x34, 0x33, 0x35, + 0x33, 0x36, 0x33, 0x37, 0x33, 0x38, 0x33, 0x39, 0x34, 0x30, 0x34, 0x31, + 0x34, 0x32, 0x34, 0x33, 0x34, 0x34, 0x34, 0x35, 0x34, 0x36, 0x34, 0x37, + 0x34, 0x38, 0x34, 0x39, 0x35, 0x30, 0x35, 0x31, 0x35, 0x32, 0x35, 0x33, + 0x35, 0x34, 0x35, 0x35, 0x35, 0x36, 0x35, 0x37, 0x35, 0x38, 0x35, 0x39, + 0x36, 0x30, 0x36, 0x31, 0x36, 0x32, 0x36, 0x33, 0x36, 0x34, 0x36, 0x35, + 0x36, 0x36, 0x36, 0x37, 0x36, 0x38, 0x36, 0x39, 0x37, 0x30, 0x37, 0x31, + 0x37, 0x32, 0x37, 0x33, 0x37, 0x34, 0x37, 0x35, 0x37, 0x36, 0x37, 0x37, + 0x37, 0x38, 0x37, 0x39, 0x38, 0x30, 0x38, 0x31, 0x38, 0x32, 0x38, 0x33, + 0x38, 0x34, 0x38, 0x35, 0x38, 0x36, 0x38, 0x37, 0x38, 0x38, 0x38, 0x39, + 0x39, 0x30, 0x39, 0x31, 0x39, 0x32, 0x39, 0x33, 0x39, 0x34, 0x39, 0x35, + 0x39, 0x36, 0x39, 0x37, 0x39, 0x38, 0x39, 0x39, +}; +} // namespace internal + +template +simdjson_inline void string_builder::append(number_type v) noexcept { + static_assert(std::is_same::value || + std::is_integral::value || + std::is_floating_point::value, + "Unsupported number type"); + // If C++17 is available, we can 'if constexpr' here. + SIMDJSON_IF_CONSTEXPR(std::is_same::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::value) { + // Process 4 digits at a time instead of 2, reducing store operations + // and divisions by approximately half for large numbers. + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + unsigned_type pv = static_cast(v); + size_t dc = internal::digit_count(pv); + char *write_pointer = buffer.get() + position + dc - 1; + + // Process 4 digits per iteration for large numbers + while (pv >= 10000) { + unsigned_type q = pv / 10000; + unsigned_type r = pv % 10000; + unsigned_type r_hi = r / 100; // High 2 digits of remainder + unsigned_type r_lo = r % 100; // Low 2 digits of remainder + // Write low 2 digits first (rightmost), then high 2 digits + memcpy(write_pointer - 1, &internal::decimal_table[r_lo * 2], 2); + memcpy(write_pointer - 3, &internal::decimal_table[r_hi * 2], 2); + write_pointer -= 4; + pv = q; + } + + // Handle remaining 1-4 digits with original 2-digit loop + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_integral::value) { + // Same 4-digit batching as unsigned path for signed integers + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + bool negative = v < 0; + unsigned_type pv = static_cast(v); + if (negative) { + pv = 0 - pv; // the 0 is for Microsoft + } + size_t dc = internal::digit_count(pv); + // by always writing the minus sign, we avoid the branch. + buffer.get()[position] = '-'; + position += negative ? 1 : 0; + char *write_pointer = buffer.get() + position + dc - 1; + + // Process 4 digits per iteration for large numbers + while (pv >= 10000) { + unsigned_type q = pv / 10000; + unsigned_type r = pv % 10000; + unsigned_type r_hi = r / 100; + unsigned_type r_lo = r % 100; + memcpy(write_pointer - 1, &internal::decimal_table[r_lo * 2], 2); + memcpy(write_pointer - 3, &internal::decimal_table[r_hi * 2], 2); + write_pointer -= 4; + pv = q; + } + + // Handle remaining 1-4 digits + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_floating_point::value) { + constexpr size_t max_number_size = 24; + if (capacity_check(max_number_size)) { + // 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. + 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. + 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 +simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept { + escape_and_append_with_quotes(constevalutil::string_constant::value); +} +#endif + +simdjson_inline void string_builder::append_raw(const char *c) noexcept { + size_t len = std::strlen(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; + } +} +#if SIMDJSON_SUPPORTS_CONCEPTS +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +simdjson_inline void string_builder::append(const T &opt) { + if (opt) { + append(*opt); + } else { + append_null(); + } +} + +template + requires(require_custom_serialization) +simdjson_inline void string_builder::append(T &&val) { + serialize(*this, std::forward(val)); +} + +template + requires(std::is_convertible::value || + std::is_same::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 + requires(!std::is_convertible::value && !require_custom_serialization) +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>) { + 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 +string_builder::view() const noexcept { + if (!is_valid) { + return simdjson::OUT_OF_CAPACITY; + } + return std::string_view(buffer.get(), position); +} + +simdjson_inline simdjson_result 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 +simdjson_inline void +string_builder::append_key_value(key_type key, value_type value) noexcept { + static_assert(std::is_same::value || + std::is_convertible::value, + "Unsupported key type"); + escape_and_append_with_quotes(key); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(value); + } +} + +#if SIMDJSON_SUPPORTS_CONCEPTS +template +simdjson_inline void +string_builder::append_key_value(value_type value) noexcept { + escape_and_append_with_quotes(); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(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 // 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 struct simd8; +template 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 +#include + +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); +} + +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t *result) { + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); +} + +} // 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 + +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 + 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(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; + + // Forward-declared so they can be used by splat and friends. + template + struct simd8; + + template> + struct base8: base> { + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m256i _value) : base>(_value) {} + + friend simdjson_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return __lasx_xvseq_b(lhs, rhs); } + + static const int SIZE = sizeof(base>::value); + + template + simdjson_inline simd8 prev(const simd8 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: base8 { + static simdjson_inline simd8 splat(bool _value) { return __lasx_xvreplgr2vr_b(uint8_t(-(!!_value))); } + + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m256i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(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 operator~() const { return *this ^ true; } + }; + + template + struct base8_numeric: base8 { + static simdjson_inline simd8 splat(T _value) { + return __lasx_xvreplgr2vr_b(_value); + } + static simdjson_inline simd8 zero() { return __lasx_xvldi(0); } + static simdjson_inline simd8 load(const T values[32]) { + return __lasx_xvld(reinterpret_cast(values), 0); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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( + 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() {} + simdjson_inline base8_numeric(const __m256i _value) : base8(_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 operator+(const simd8 other) const { return __lasx_xvadd_b(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return __lasx_xvsub_b(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } + + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } + + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 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 + 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(reinterpret_cast(pshufb_combine_table)), pop1 * 8); + __m256i maskhi = __lasx_xvldx(reinterpret_cast(reinterpret_cast(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(output), 0); + uint64_t value3 = __lasx_xvpickve2gr_du(answer, 2); + uint64_t value4 = __lasx_xvpickve2gr_du(answer, 3); + uint64_t *pos = reinterpret_cast(reinterpret_cast(output) + 16 - (pop1 + pop2) / 2); + pos[0] = value3; + pos[1] = value4; + } + + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; + + // Signed bytes + template<> + struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( + 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 max_val(const simd8 other) const { return __lasx_xvmax_b(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return __lasx_xvmin_b(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return __lasx_xvslt_b(other, *this); } + simdjson_inline simd8 operator<(const simd8 other) const { return __lasx_xvslt_b(*this, other); } + }; + + // Unsigned bytes + template<> + struct simd8: base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( + 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 saturating_add(const simd8 other) const { return __lasx_xvsadd_bu(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return __lasx_xvssub_bu(*this, other); } + + // Order-specific operations + simdjson_inline simd8 max_val(const simd8 other) const { return __lasx_xvmax_bu(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return __lasx_xvmin_bu(other, *this); } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } + simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } + simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } + simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } + + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } + simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } + simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } + simdjson_inline simd8 any_bits_set(simd8 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 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 bits) const { return !bits_not_set_anywhere(bits); } + template + simdjson_inline simd8 shr() const { return simd8(__lasx_xvsrli_b(*this, N)); } + template + simdjson_inline simd8 shl() const { return simd8(__lasx_xvslli_b(*this, N)); } + }; + + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 2, "LASX kernel should use two registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } + + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed + + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1) : chunks{chunk0, chunk1} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::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)*0); + this->chunks[1].store(ptr+sizeof(simd8)*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 reduce_or() const { + return this->chunks[0] | this->chunks[1]; + } + + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] == mask, + this->chunks[1] == mask + ).to_bitmask(); + } + + simdjson_inline uint64_t eq(const simd8x64 &other) const { + return simd8x64( + 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 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] <= mask, + this->chunks[1] <= mask + ).to_bitmask(); + } + }; // struct simd8x64 + +} // 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 v(src); + v.store(dst); + return { + static_cast((v == '\\').to_bitmask()), // bs_bits + static_cast((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 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + static_cast((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 +struct has_custom_serialization : std::false_type {}; + +inline constexpr struct serialize_tag { + template + constexpr void operator()(lasx::builder::string_builder& b, T&& obj) const{ + return tag_invoke(*this, b, std::forward(obj)); + } + + +} serialize{}; +template +struct has_custom_serialization(), std::declval())) +>> : std::true_type {}; + +template +constexpr bool require_custom_serialization = has_custom_serialization::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::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 + 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 + simdjson_inline void append_key_value(key_type key, value_type value) noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + template + simdjson_inline void append_key_value(value_type value) noexcept; + + // Support for optional types (std::optional, etc.) + template + requires(!require_custom_serialization) + simdjson_inline void append(const T &opt); + + template + requires(require_custom_serialization) + simdjson_inline void append(T &&val); + + // Support for string-like types + template + requires(std::is_convertible::value || + std::is_same::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 +requires (!std::is_convertible::value && !require_custom_serialization) + 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; +#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 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 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; + +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); + + /** + * 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. + */ + simdjson_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 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 +simdjson_warn_unused simdjson_result 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 +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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ +#if SIMDJSON_STATIC_REFLECTION + +#include +#include +#include +#include +#include +#include +#include +#include +// #include // for std::define_static_string - header not available yet + +namespace simdjson { +namespace lasx { +namespace builder { + +template + requires(concepts::container_but_not_string && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + auto it = t.begin(); + auto end = t.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +constexpr void atom(string_builder &b, const T &t) { + b.escape_and_append_with_quotes(t); +} + +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &m) { + if (m.empty()) { + b.append_raw("{}"); + return; + } + b.append('{'); + bool first = true; + for (const auto& [key, value] : m) { + if (!first) { + b.append(','); + } + first = false; + // Keys must be convertible to string_view per the concept + b.escape_and_append_with_quotes(key); + b.append(':'); + atom(b, value); + } + b.append('}'); +} + + +template::value && !std::is_same_v>::type> +constexpr void atom(string_builder &b, const number_type t) { + b.append(t); +} + +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, t.[:dm:]); + i++; + }; + b.append('}'); +} + +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &opt) { + if (opt) { + atom(b, opt.value()); + } else { + b.append_raw("null"); + } +} + +// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &ptr) { + if (ptr) { + atom(b, *ptr); + } else { + b.append_raw("null"); + } +} + +// Support for enums - serialize as string representation using expand approach from P2996R12 +template + requires(std::is_enum_v && !require_custom_serialization) +void atom(string_builder &b, const T &e) { +#if SIMDJSON_STATIC_REFLECTION + 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(std::meta::identifier_of(enum_val))); + if (e == [:enum_val:]) { + b.append_raw(enum_str); + return; + } + }; + // Fallback to integer if enum value not found + atom(b, static_cast>(e)); +#else + // Fallback: serialize as integer if reflection not available + atom(b, static_cast>(e)); +#endif +} + +// Support for appendable containers that don't have operator[] (sets, etc.) +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &container) { + if (container.empty()) { + b.append_raw("[]"); + return; + } + b.append('['); + bool first = true; + for (const auto& item : container) { + if (!first) { + b.append(','); + } + first = false; + atom(b, item); + } + b.append(']'); +} + +// append functions that delegate to atom functions for primitive types +template + requires(std::is_arithmetic_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +// works for struct +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^Z, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, z.[:dm:]); + i++; + }; + b.append('}'); +} + +// works for container that have begin() and end() iterators +template + requires(concepts::container_but_not_string && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + auto it = z.begin(); + auto end = z.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} + +template + requires (require_custom_serialization) +void append(string_builder &b, const Z &z) { + b.append(z); +} + + +template +simdjson_warn_unused simdjson_result to_json_string(const Z &z, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +template +simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + 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 +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 + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +void extract_from(string_builder &b, const T &obj) { + // 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) || ...); + }; + + b.append('{'); + bool first = true; + + // 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_public(mem)) { + 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 (should_extract(key)) { + if (!first) { + b.append(','); + } + first = false; + + // Serialize the key + constexpr auto quoted_key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem))); + b.append_raw(quoted_key); + b.append(':'); + + // Serialize the value + atom(b, obj.[:mem:]); + } + } + }; + + b.append('}'); +} + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + extract_from(b, obj); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +} // namespace builder +} // namespace lasx +// Alias the function template to 'to' in the global namespace +template +simdjson_warn_unused simdjson_result to_json(const Z &z, size_t initial_capacity = lasx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + lasx::builder::string_builder b(initial_capacity); + lasx::builder::append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} +template +simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = lasx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + lasx::builder::string_builder b(initial_capacity); + lasx::builder::append(b, z); + std::string_view view; + if(auto e = b.view().get(view); e) { return e; } + s.assign(view); + return SUCCESS; +} +// Global namespace function for extract_from +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result 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(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 +simdjson_warn_unused simdjson_result 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 + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result 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(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 +simdjson_warn_unused simdjson_result 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 + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result 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(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 +#include +#include +#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 SIMDJSON_EXPERIMENTAL_HAS_NEON +#include +#ifdef _MSC_VER +#include +#endif +#endif +#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 +#include +#ifdef _MSC_VER +#include +#endif +#endif + +namespace simdjson { +namespace lasx { +namespace builder { + +static SIMDJSON_CONSTEXPR_LAMBDA std::array + 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; +} + +**/ + +SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline bool +simple_needs_escaping(std::string_view v) { + for (char c : v) { + // a table lookup is faster than a series of comparisons + if (json_quotable_character[static_cast(c)]) { + return true; + } + } + return false; +} + +#if SIMDJSON_EXPERIMENTAL_HAS_NEON +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + uint8x16_t running = vdupq_n_u8(0); + uint8x16_t v34 = vdupq_n_u8(34); + uint8x16_t v92 = vdupq_n_u8(92); + + for (; i + 15 < view.size(); i += 16) { + uint8x16_t word = vld1q_u8((const uint8_t *)view.data() + i); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + if (i < view.size()) { + uint8x16_t word = + vld1q_u8((const uint8_t *)view.data() + view.length() - 16); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + return vmaxvq_u32(vreinterpretq_u32_u8(running)) != 0; +} +#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2 +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + __m128i running = _mm_setzero_si128(); + for (; i + 15 < view.size(); i += 16) { + + __m128i word = + _mm_loadu_si128(reinterpret_cast(view.data() + i)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + if (i < view.size()) { + __m128i word = _mm_loadu_si128( + reinterpret_cast(view.data() + view.length() - 16)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + return _mm_movemask_epi8(running) != 0; +} +#else +simdjson_inline bool fast_needs_escaping(std::string_view view) { + return simple_needs_escaping(view); +} +#endif + +// 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(*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(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)); + + if (vmaxvq_u32(vreinterpretq_u32_u8(needs_escape)) != 0) { + // Found quotable character - extract exact byte position using ctz + uint64x2_t as64 = vreinterpretq_u64_u8(needs_escape); + uint64_t lo = vgetq_lane_u64(as64, 0); + uint64_t hi = vgetq_lane_u64(as64, 1); + size_t offset = ptr - reinterpret_cast(view.data()); +#ifdef _MSC_VER + unsigned long trailing_zero = 0; + if (lo != 0) { + _BitScanForward64(&trailing_zero, lo); + return offset + trailing_zero / 8; + } else { + _BitScanForward64(&trailing_zero, hi); + return offset + 8 + trailing_zero / 8; + } +#else + if (lo != 0) { + return offset + __builtin_ctzll(lo) / 8; + } else { + return offset + 8 + __builtin_ctzll(hi) / 8; + } +#endif + } + 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(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(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(view.data()); +#ifdef _MSC_VER + unsigned long trailing_zero = 0; + _BitScanForward(&trailing_zero, mask); + return offset + trailing_zero; +#else + return offset + __builtin_ctz(mask); +#endif + } + 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(); + } +} + +// 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 out - initout; +} + +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; +} + +simdjson_inline void string_builder::grow_buffer(size_t desired_capacity) { + if (!is_valid) { + return; + } + std::unique_ptr 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 { + +template ::value>::type> +simdjson_really_inline int int_log2(number_type x) { + return 63 - leading_zeroes(uint64_t(x) | 1); +} + +simdjson_really_inline int fast_digit_count_32(uint32_t x) { + static uint64_t table[] = { + 4294967296, 8589934582, 8589934582, 8589934582, 12884901788, + 12884901788, 12884901788, 17179868184, 17179868184, 17179868184, + 21474826480, 21474826480, 21474826480, 21474826480, 25769703776, + 25769703776, 25769703776, 30063771072, 30063771072, 30063771072, + 34349738368, 34349738368, 34349738368, 34349738368, 38554705664, + 38554705664, 38554705664, 41949672960, 41949672960, 41949672960, + 42949672960, 42949672960}; + return uint32_t((x + table[int_log2(x)]) >> 32); +} + +simdjson_really_inline int fast_digit_count_64(uint64_t x) { + static uint64_t table[] = {9, + 99, + 999, + 9999, + 99999, + 999999, + 9999999, + 99999999, + 999999999, + 9999999999, + 99999999999, + 999999999999, + 9999999999999, + 99999999999999, + 999999999999999ULL, + 9999999999999999ULL, + 99999999999999999ULL, + 999999999999999999ULL, + 9999999999999999999ULL}; + int y = (19 * int_log2(x) >> 6); + y += x > table[y]; + return y + 1; +} + +template ::value>::type> +simdjson_really_inline size_t digit_count(number_type v) noexcept { + static_assert(sizeof(number_type) == 8 || sizeof(number_type) == 4 || + sizeof(number_type) == 2 || sizeof(number_type) == 1, + "We only support 8-bit, 16-bit, 32-bit and 64-bit numbers"); + SIMDJSON_IF_CONSTEXPR(sizeof(number_type) <= 4) { + return fast_digit_count_32(static_cast(v)); + } + else { + return fast_digit_count_64(static_cast(v)); + } +} +static const char decimal_table[200] = { + 0x30, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35, + 0x30, 0x36, 0x30, 0x37, 0x30, 0x38, 0x30, 0x39, 0x31, 0x30, 0x31, 0x31, + 0x31, 0x32, 0x31, 0x33, 0x31, 0x34, 0x31, 0x35, 0x31, 0x36, 0x31, 0x37, + 0x31, 0x38, 0x31, 0x39, 0x32, 0x30, 0x32, 0x31, 0x32, 0x32, 0x32, 0x33, + 0x32, 0x34, 0x32, 0x35, 0x32, 0x36, 0x32, 0x37, 0x32, 0x38, 0x32, 0x39, + 0x33, 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x33, 0x34, 0x33, 0x35, + 0x33, 0x36, 0x33, 0x37, 0x33, 0x38, 0x33, 0x39, 0x34, 0x30, 0x34, 0x31, + 0x34, 0x32, 0x34, 0x33, 0x34, 0x34, 0x34, 0x35, 0x34, 0x36, 0x34, 0x37, + 0x34, 0x38, 0x34, 0x39, 0x35, 0x30, 0x35, 0x31, 0x35, 0x32, 0x35, 0x33, + 0x35, 0x34, 0x35, 0x35, 0x35, 0x36, 0x35, 0x37, 0x35, 0x38, 0x35, 0x39, + 0x36, 0x30, 0x36, 0x31, 0x36, 0x32, 0x36, 0x33, 0x36, 0x34, 0x36, 0x35, + 0x36, 0x36, 0x36, 0x37, 0x36, 0x38, 0x36, 0x39, 0x37, 0x30, 0x37, 0x31, + 0x37, 0x32, 0x37, 0x33, 0x37, 0x34, 0x37, 0x35, 0x37, 0x36, 0x37, 0x37, + 0x37, 0x38, 0x37, 0x39, 0x38, 0x30, 0x38, 0x31, 0x38, 0x32, 0x38, 0x33, + 0x38, 0x34, 0x38, 0x35, 0x38, 0x36, 0x38, 0x37, 0x38, 0x38, 0x38, 0x39, + 0x39, 0x30, 0x39, 0x31, 0x39, 0x32, 0x39, 0x33, 0x39, 0x34, 0x39, 0x35, + 0x39, 0x36, 0x39, 0x37, 0x39, 0x38, 0x39, 0x39, +}; +} // namespace internal + +template +simdjson_inline void string_builder::append(number_type v) noexcept { + static_assert(std::is_same::value || + std::is_integral::value || + std::is_floating_point::value, + "Unsupported number type"); + // If C++17 is available, we can 'if constexpr' here. + SIMDJSON_IF_CONSTEXPR(std::is_same::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::value) { + // Process 4 digits at a time instead of 2, reducing store operations + // and divisions by approximately half for large numbers. + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + unsigned_type pv = static_cast(v); + size_t dc = internal::digit_count(pv); + char *write_pointer = buffer.get() + position + dc - 1; + + // Process 4 digits per iteration for large numbers + while (pv >= 10000) { + unsigned_type q = pv / 10000; + unsigned_type r = pv % 10000; + unsigned_type r_hi = r / 100; // High 2 digits of remainder + unsigned_type r_lo = r % 100; // Low 2 digits of remainder + // Write low 2 digits first (rightmost), then high 2 digits + memcpy(write_pointer - 1, &internal::decimal_table[r_lo * 2], 2); + memcpy(write_pointer - 3, &internal::decimal_table[r_hi * 2], 2); + write_pointer -= 4; + pv = q; + } + + // Handle remaining 1-4 digits with original 2-digit loop + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_integral::value) { + // Same 4-digit batching as unsigned path for signed integers + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + bool negative = v < 0; + unsigned_type pv = static_cast(v); + if (negative) { + pv = 0 - pv; // the 0 is for Microsoft + } + size_t dc = internal::digit_count(pv); + // by always writing the minus sign, we avoid the branch. + buffer.get()[position] = '-'; + position += negative ? 1 : 0; + char *write_pointer = buffer.get() + position + dc - 1; + + // Process 4 digits per iteration for large numbers + while (pv >= 10000) { + unsigned_type q = pv / 10000; + unsigned_type r = pv % 10000; + unsigned_type r_hi = r / 100; + unsigned_type r_lo = r % 100; + memcpy(write_pointer - 1, &internal::decimal_table[r_lo * 2], 2); + memcpy(write_pointer - 3, &internal::decimal_table[r_hi * 2], 2); + write_pointer -= 4; + pv = q; + } + + // Handle remaining 1-4 digits + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_floating_point::value) { + constexpr size_t max_number_size = 24; + if (capacity_check(max_number_size)) { + // 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. + 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. + 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 +simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept { + escape_and_append_with_quotes(constevalutil::string_constant::value); +} +#endif + +simdjson_inline void string_builder::append_raw(const char *c) noexcept { + size_t len = std::strlen(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; + } +} +#if SIMDJSON_SUPPORTS_CONCEPTS +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +simdjson_inline void string_builder::append(const T &opt) { + if (opt) { + append(*opt); + } else { + append_null(); + } +} + +template + requires(require_custom_serialization) +simdjson_inline void string_builder::append(T &&val) { + serialize(*this, std::forward(val)); +} + +template + requires(std::is_convertible::value || + std::is_same::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 + requires(!std::is_convertible::value && !require_custom_serialization) +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>) { + 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 +string_builder::view() const noexcept { + if (!is_valid) { + return simdjson::OUT_OF_CAPACITY; + } + return std::string_view(buffer.get(), position); +} + +simdjson_inline simdjson_result 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 +simdjson_inline void +string_builder::append_key_value(key_type key, value_type value) noexcept { + static_assert(std::is_same::value || + std::is_convertible::value, + "Unsupported key type"); + escape_and_append_with_quotes(key); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(value); + } +} + +#if SIMDJSON_SUPPORTS_CONCEPTS +template +simdjson_inline void +string_builder::append_key_value(value_type value) noexcept { + escape_and_append_with_quotes(); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(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 + +#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 +#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); +} + +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); +} + +} // 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 + struct simd8; + + // SIMD byte mask type (returned by things like eq and gt) + template<> + struct simd8 { + 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 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 operator|(const simd8 other) const { return __riscv_vmor(*this, other, VL8); } + simdjson_inline simd8 operator&(const simd8 other) const { return __riscv_vmand(*this, other, VL8); } + simdjson_inline simd8 operator^(const simd8 other) const { return __riscv_vmxor(*this, other, VL8); } + simdjson_inline simd8 bit_andnot(const simd8 other) const { return __riscv_vmandn(other, *this, VL8); } + simdjson_inline simd8 operator~() const { return __riscv_vmnot(*this, VL8); } + simdjson_inline simd8& operator|=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline simd8& operator&=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline simd8& operator^=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; + + // Unsigned bytes + template<> + struct simd8 { + + 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 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 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( + 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 operator|(const simd8 other) const { return __riscv_vor_vv_u8m1( value, other, VL8); } + simdjson_inline simd8 operator&(const simd8 other) const { return __riscv_vand_vv_u8m1( value, other, VL8); } + simdjson_inline simd8 operator^(const simd8 other) const { return __riscv_vxor_vv_u8m1( value, other, VL8); } + simdjson_inline simd8 operator~() const { return __riscv_vnot_v_u8m1(value, VL8); } +#if __riscv_zvbb + simdjson_inline simd8 bit_andnot(const simd8 other) const { return __riscv_vandn_vv_u8m1(other, value, VL8); } +#else + simdjson_inline simd8 bit_andnot(const simd8 other) const { return other & ~*this; } +#endif + simdjson_inline simd8& operator|=(const simd8 other) { value = *this | other; return *this; } + simdjson_inline simd8& operator&=(const simd8 other) { value = *this & other; return *this; } + simdjson_inline simd8& operator^=(const simd8 other) { value = *this ^ other; return *this; } + + simdjson_inline simd8 operator==(const simd8 other) const { return __riscv_vmseq(value, other, VL8); } + simdjson_inline simd8 operator==(uint8_t other) const { return __riscv_vmseq(value, other, VL8); } + + template + simdjson_inline simd8 prev(const simd8 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 saturating_add(const simd8 other) const { return __riscv_vsaddu(value, other, VL8); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return __riscv_vssubu(value, other, VL8); } + + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { return __riscv_vadd(value, other, VL8); } + simdjson_inline simd8 operator-(const simd8 other) const { return __riscv_vsub(value, other, VL8); } + simdjson_inline simd8& operator+=(const simd8 other) { value = *this + other; return *this; } + simdjson_inline simd8& operator-=(const simd8 other) { value = *this - other; return *this; } + + // Order-specific operations + simdjson_inline simd8 operator<=(const simd8 other) const { return __riscv_vmsleu(value, other, VL8); } + simdjson_inline simd8 operator>=(const simd8 other) const { return __riscv_vmsgeu(value, other, VL8); } + simdjson_inline simd8 operator<(const simd8 other) const { return __riscv_vmsltu(value, other, VL8); } + simdjson_inline simd8 operator>(const simd8 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 gt_bits(const simd8 other) const { return simd8(*this > other); } + // Same as <, but instead of guaranteeing all 1's == true, false = 0 and true = nonzero. + simdjson_inline simd8 lt_bits(const simd8 other) const { return simd8(*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 bits) const { return (*this & bits).any_bits_set_anywhere(); } + template + simdjson_inline simd8 shr() const { return __riscv_vsrl(value, N, VL8); } + template + simdjson_inline simd8 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 + simdjson_inline simd8 lookup_16(simd8 lookup_table) const { + return __riscv_vrgather(lookup_table, value, VL8); + } + + // compress inactive elements, to match AVX-512 behavior + template + 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 + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; + + // Signed bytes + template<> + struct simd8 { + 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 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( + 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() const { return __riscv_vreinterpret_u8m1(value); } + + // Math + simdjson_inline simd8 operator+(const simd8 other) const { return __riscv_vadd(value, other, VL8); } + simdjson_inline simd8 operator-(const simd8 other) const { return __riscv_vsub(value, other, VL8); } + simdjson_inline simd8& operator+=(const simd8 other) { value = *this + other; return *this; } + simdjson_inline simd8& operator-=(const simd8 other) { value = *this - other; return *this; } + + // Order-sensitive comparisons + simdjson_inline simd8 max_val( const simd8 other) const { return __riscv_vmax( value, other, VL8); } + simdjson_inline simd8 min_val( const simd8 other) const { return __riscv_vmin( value, other, VL8); } + simdjson_inline simd8 operator>( const simd8 other) const { return __riscv_vmsgt(value, other, VL8); } + simdjson_inline simd8 operator<( const simd8 other) const { return __riscv_vmslt(value, other, VL8); } + simdjson_inline simd8 operator==(const simd8 other) const { return __riscv_vmseq(value, other, VL8); } + + template + simdjson_inline simd8 prev(const simd8 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 + simdjson_inline simd8 lookup_16(simd8 lookup_table) const { + return __riscv_vrgather(lookup_table, value, VL8); + } + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; + + template + struct simd8x64; + template<> + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + vuint8x64_t value; + +#if __riscv_v_fixed_vlen >= 512 + template simd8 get() const { return value; } +#else + template simd8 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& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& 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 + +} // 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); + 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 v(src); + v.store(dst); + return { (v == '\\').to_bitmask(), (v == '"').to_bitmask() }; +} + +struct escaping { + static constexpr uint64_t BYTES_PROCESSED = sizeof(simd8); + 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 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 + +#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(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 +struct has_custom_serialization : std::false_type {}; + +inline constexpr struct serialize_tag { + template + constexpr void operator()(rvv_vls::builder::string_builder& b, T&& obj) const{ + return tag_invoke(*this, b, std::forward(obj)); + } + + +} serialize{}; +template +struct has_custom_serialization(), std::declval())) +>> : std::true_type {}; + +template +constexpr bool require_custom_serialization = has_custom_serialization::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::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 + 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 + simdjson_inline void append_key_value(key_type key, value_type value) noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + template + simdjson_inline void append_key_value(value_type value) noexcept; + + // Support for optional types (std::optional, etc.) + template + requires(!require_custom_serialization) + simdjson_inline void append(const T &opt); + + template + requires(require_custom_serialization) + simdjson_inline void append(T &&val); + + // Support for string-like types + template + requires(std::is_convertible::value || + std::is_same::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 +requires (!std::is_convertible::value && !require_custom_serialization) + 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; +#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 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 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; + +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); + + /** + * 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. + */ + simdjson_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 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 +simdjson_warn_unused simdjson_result 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 +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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ +#if SIMDJSON_STATIC_REFLECTION + +#include +#include +#include +#include +#include +#include +#include +#include +// #include // for std::define_static_string - header not available yet + +namespace simdjson { +namespace rvv_vls { +namespace builder { + +template + requires(concepts::container_but_not_string && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + auto it = t.begin(); + auto end = t.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +constexpr void atom(string_builder &b, const T &t) { + b.escape_and_append_with_quotes(t); +} + +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &m) { + if (m.empty()) { + b.append_raw("{}"); + return; + } + b.append('{'); + bool first = true; + for (const auto& [key, value] : m) { + if (!first) { + b.append(','); + } + first = false; + // Keys must be convertible to string_view per the concept + b.escape_and_append_with_quotes(key); + b.append(':'); + atom(b, value); + } + b.append('}'); +} + + +template::value && !std::is_same_v>::type> +constexpr void atom(string_builder &b, const number_type t) { + b.append(t); +} + +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &t) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, t.[:dm:]); + i++; + }; + b.append('}'); +} + +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &opt) { + if (opt) { + atom(b, opt.value()); + } else { + b.append_raw("null"); + } +} + +// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.) +template + requires(!require_custom_serialization) +constexpr void atom(string_builder &b, const T &ptr) { + if (ptr) { + atom(b, *ptr); + } else { + b.append_raw("null"); + } +} + +// Support for enums - serialize as string representation using expand approach from P2996R12 +template + requires(std::is_enum_v && !require_custom_serialization) +void atom(string_builder &b, const T &e) { +#if SIMDJSON_STATIC_REFLECTION + 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(std::meta::identifier_of(enum_val))); + if (e == [:enum_val:]) { + b.append_raw(enum_str); + return; + } + }; + // Fallback to integer if enum value not found + atom(b, static_cast>(e)); +#else + // Fallback: serialize as integer if reflection not available + atom(b, static_cast>(e)); +#endif +} + +// Support for appendable containers that don't have operator[] (sets, etc.) +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +constexpr void atom(string_builder &b, const T &container) { + if (container.empty()) { + b.append_raw("[]"); + return; + } + b.append('['); + bool first = true; + for (const auto& item : container) { + if (!first) { + b.append(','); + } + first = false; + atom(b, item); + } + b.append(']'); +} + +// append functions that delegate to atom functions for primitive types +template + requires(std::is_arithmetic_v && !std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!concepts::container_but_not_string && !concepts::string_view_keyed_map && + !concepts::optional_type && !concepts::smart_pointer && + !std::is_same_v && + !std::is_same_v && !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +template + requires(!require_custom_serialization) +void append(string_builder &b, const T &t) { + atom(b, t); +} + +// works for struct +template + requires(std::is_class_v && !concepts::container_but_not_string && + !concepts::string_view_keyed_map && + !concepts::optional_type && + !concepts::smart_pointer && + !concepts::appendable_containers && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + int i = 0; + b.append('{'); + template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^Z, std::meta::access_context::unchecked()))) { + if (i != 0) + b.append(','); + constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm))); + b.append_raw(key); + b.append(':'); + atom(b, z.[:dm:]); + i++; + }; + b.append('}'); +} + +// works for container that have begin() and end() iterators +template + requires(concepts::container_but_not_string && !require_custom_serialization) +void append(string_builder &b, const Z &z) { + auto it = z.begin(); + auto end = z.end(); + if (it == end) { + b.append_raw("[]"); + return; + } + b.append('['); + atom(b, *it); + ++it; + for (; it != end; ++it) { + b.append(','); + atom(b, *it); + } + b.append(']'); +} + +template + requires (require_custom_serialization) +void append(string_builder &b, const Z &z) { + b.append(z); +} + + +template +simdjson_warn_unused simdjson_result to_json_string(const Z &z, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +template +simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + 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 +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 + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +void extract_from(string_builder &b, const T &obj) { + // 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) || ...); + }; + + b.append('{'); + bool first = true; + + // 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_public(mem)) { + 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 (should_extract(key)) { + if (!first) { + b.append(','); + } + first = false; + + // Serialize the key + constexpr auto quoted_key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem))); + b.append_raw(quoted_key); + b.append(':'); + + // Serialize the value + atom(b, obj.[:mem:]); + } + } + }; + + b.append('}'); +} + +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); + extract_from(b, obj); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} + +} // namespace builder +} // namespace rvv_vls +// Alias the function template to 'to' in the global namespace +template +simdjson_warn_unused simdjson_result to_json(const Z &z, size_t initial_capacity = rvv_vls::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + rvv_vls::builder::string_builder b(initial_capacity); + rvv_vls::builder::append(b, z); + std::string_view s; + if(auto e = b.view().get(s); e) { return e; } + return std::string(s); +} +template +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) { + rvv_vls::builder::string_builder b(initial_capacity); + rvv_vls::builder::append(b, z); + std::string_view view; + if(auto e = b.view().get(view); e) { return e; } + s.assign(view); + return SUCCESS; +} +// Global namespace function for extract_from +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result 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(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 +simdjson_warn_unused simdjson_result 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 + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result 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(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 +simdjson_warn_unused simdjson_result 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 + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_result 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(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 +#include +#include +#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 SIMDJSON_EXPERIMENTAL_HAS_NEON +#include +#ifdef _MSC_VER +#include +#endif +#endif +#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 +#include +#ifdef _MSC_VER +#include +#endif +#endif + +namespace simdjson { +namespace rvv_vls { +namespace builder { + +static SIMDJSON_CONSTEXPR_LAMBDA std::array + 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; +} + +**/ + +SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline bool +simple_needs_escaping(std::string_view v) { + for (char c : v) { + // a table lookup is faster than a series of comparisons + if (json_quotable_character[static_cast(c)]) { + return true; + } + } + return false; +} + +#if SIMDJSON_EXPERIMENTAL_HAS_NEON +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + uint8x16_t running = vdupq_n_u8(0); + uint8x16_t v34 = vdupq_n_u8(34); + uint8x16_t v92 = vdupq_n_u8(92); + + for (; i + 15 < view.size(); i += 16) { + uint8x16_t word = vld1q_u8((const uint8_t *)view.data() + i); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + if (i < view.size()) { + uint8x16_t word = + vld1q_u8((const uint8_t *)view.data() + view.length() - 16); + running = vorrq_u8(running, vceqq_u8(word, v34)); + running = vorrq_u8(running, vceqq_u8(word, v92)); + running = vorrq_u8(running, vcltq_u8(word, vdupq_n_u8(32))); + } + return vmaxvq_u32(vreinterpretq_u32_u8(running)) != 0; +} +#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2 +simdjson_inline bool fast_needs_escaping(std::string_view view) { + if (view.size() < 16) { + return simple_needs_escaping(view); + } + size_t i = 0; + __m128i running = _mm_setzero_si128(); + for (; i + 15 < view.size(); i += 16) { + + __m128i word = + _mm_loadu_si128(reinterpret_cast(view.data() + i)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + if (i < view.size()) { + __m128i word = _mm_loadu_si128( + reinterpret_cast(view.data() + view.length() - 16)); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34))); + running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92))); + running = _mm_or_si128( + running, _mm_cmpeq_epi8(_mm_subs_epu8(word, _mm_set1_epi8(31)), + _mm_setzero_si128())); + } + return _mm_movemask_epi8(running) != 0; +} +#else +simdjson_inline bool fast_needs_escaping(std::string_view view) { + return simple_needs_escaping(view); +} +#endif + +// 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(*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(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)); + + if (vmaxvq_u32(vreinterpretq_u32_u8(needs_escape)) != 0) { + // Found quotable character - extract exact byte position using ctz + uint64x2_t as64 = vreinterpretq_u64_u8(needs_escape); + uint64_t lo = vgetq_lane_u64(as64, 0); + uint64_t hi = vgetq_lane_u64(as64, 1); + size_t offset = ptr - reinterpret_cast(view.data()); +#ifdef _MSC_VER + unsigned long trailing_zero = 0; + if (lo != 0) { + _BitScanForward64(&trailing_zero, lo); + return offset + trailing_zero / 8; + } else { + _BitScanForward64(&trailing_zero, hi); + return offset + 8 + trailing_zero / 8; + } +#else + if (lo != 0) { + return offset + __builtin_ctzll(lo) / 8; + } else { + return offset + 8 + __builtin_ctzll(hi) / 8; + } +#endif + } + 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(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(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(view.data()); +#ifdef _MSC_VER + unsigned long trailing_zero = 0; + _BitScanForward(&trailing_zero, mask); + return offset + trailing_zero; +#else + return offset + __builtin_ctz(mask); +#endif + } + 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(); + } +} + +// 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 out - initout; +} + +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; +} + +simdjson_inline void string_builder::grow_buffer(size_t desired_capacity) { + if (!is_valid) { + return; + } + std::unique_ptr 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 { + +template ::value>::type> +simdjson_really_inline int int_log2(number_type x) { + return 63 - leading_zeroes(uint64_t(x) | 1); +} + +simdjson_really_inline int fast_digit_count_32(uint32_t x) { + static uint64_t table[] = { + 4294967296, 8589934582, 8589934582, 8589934582, 12884901788, + 12884901788, 12884901788, 17179868184, 17179868184, 17179868184, + 21474826480, 21474826480, 21474826480, 21474826480, 25769703776, + 25769703776, 25769703776, 30063771072, 30063771072, 30063771072, + 34349738368, 34349738368, 34349738368, 34349738368, 38554705664, + 38554705664, 38554705664, 41949672960, 41949672960, 41949672960, + 42949672960, 42949672960}; + return uint32_t((x + table[int_log2(x)]) >> 32); +} + +simdjson_really_inline int fast_digit_count_64(uint64_t x) { + static uint64_t table[] = {9, + 99, + 999, + 9999, + 99999, + 999999, + 9999999, + 99999999, + 999999999, + 9999999999, + 99999999999, + 999999999999, + 9999999999999, + 99999999999999, + 999999999999999ULL, + 9999999999999999ULL, + 99999999999999999ULL, + 999999999999999999ULL, + 9999999999999999999ULL}; + int y = (19 * int_log2(x) >> 6); + y += x > table[y]; + return y + 1; +} + +template ::value>::type> +simdjson_really_inline size_t digit_count(number_type v) noexcept { + static_assert(sizeof(number_type) == 8 || sizeof(number_type) == 4 || + sizeof(number_type) == 2 || sizeof(number_type) == 1, + "We only support 8-bit, 16-bit, 32-bit and 64-bit numbers"); + SIMDJSON_IF_CONSTEXPR(sizeof(number_type) <= 4) { + return fast_digit_count_32(static_cast(v)); + } + else { + return fast_digit_count_64(static_cast(v)); + } +} +static const char decimal_table[200] = { + 0x30, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35, + 0x30, 0x36, 0x30, 0x37, 0x30, 0x38, 0x30, 0x39, 0x31, 0x30, 0x31, 0x31, + 0x31, 0x32, 0x31, 0x33, 0x31, 0x34, 0x31, 0x35, 0x31, 0x36, 0x31, 0x37, + 0x31, 0x38, 0x31, 0x39, 0x32, 0x30, 0x32, 0x31, 0x32, 0x32, 0x32, 0x33, + 0x32, 0x34, 0x32, 0x35, 0x32, 0x36, 0x32, 0x37, 0x32, 0x38, 0x32, 0x39, + 0x33, 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x33, 0x34, 0x33, 0x35, + 0x33, 0x36, 0x33, 0x37, 0x33, 0x38, 0x33, 0x39, 0x34, 0x30, 0x34, 0x31, + 0x34, 0x32, 0x34, 0x33, 0x34, 0x34, 0x34, 0x35, 0x34, 0x36, 0x34, 0x37, + 0x34, 0x38, 0x34, 0x39, 0x35, 0x30, 0x35, 0x31, 0x35, 0x32, 0x35, 0x33, + 0x35, 0x34, 0x35, 0x35, 0x35, 0x36, 0x35, 0x37, 0x35, 0x38, 0x35, 0x39, + 0x36, 0x30, 0x36, 0x31, 0x36, 0x32, 0x36, 0x33, 0x36, 0x34, 0x36, 0x35, + 0x36, 0x36, 0x36, 0x37, 0x36, 0x38, 0x36, 0x39, 0x37, 0x30, 0x37, 0x31, + 0x37, 0x32, 0x37, 0x33, 0x37, 0x34, 0x37, 0x35, 0x37, 0x36, 0x37, 0x37, + 0x37, 0x38, 0x37, 0x39, 0x38, 0x30, 0x38, 0x31, 0x38, 0x32, 0x38, 0x33, + 0x38, 0x34, 0x38, 0x35, 0x38, 0x36, 0x38, 0x37, 0x38, 0x38, 0x38, 0x39, + 0x39, 0x30, 0x39, 0x31, 0x39, 0x32, 0x39, 0x33, 0x39, 0x34, 0x39, 0x35, + 0x39, 0x36, 0x39, 0x37, 0x39, 0x38, 0x39, 0x39, +}; +} // namespace internal + +template +simdjson_inline void string_builder::append(number_type v) noexcept { + static_assert(std::is_same::value || + std::is_integral::value || + std::is_floating_point::value, + "Unsupported number type"); + // If C++17 is available, we can 'if constexpr' here. + SIMDJSON_IF_CONSTEXPR(std::is_same::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::value) { + // Process 4 digits at a time instead of 2, reducing store operations + // and divisions by approximately half for large numbers. + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + unsigned_type pv = static_cast(v); + size_t dc = internal::digit_count(pv); + char *write_pointer = buffer.get() + position + dc - 1; + + // Process 4 digits per iteration for large numbers + while (pv >= 10000) { + unsigned_type q = pv / 10000; + unsigned_type r = pv % 10000; + unsigned_type r_hi = r / 100; // High 2 digits of remainder + unsigned_type r_lo = r % 100; // Low 2 digits of remainder + // Write low 2 digits first (rightmost), then high 2 digits + memcpy(write_pointer - 1, &internal::decimal_table[r_lo * 2], 2); + memcpy(write_pointer - 3, &internal::decimal_table[r_hi * 2], 2); + write_pointer -= 4; + pv = q; + } + + // Handle remaining 1-4 digits with original 2-digit loop + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_integral::value) { + // Same 4-digit batching as unsigned path for signed integers + constexpr size_t max_number_size = 20; + if (capacity_check(max_number_size)) { + using unsigned_type = typename std::make_unsigned::type; + bool negative = v < 0; + unsigned_type pv = static_cast(v); + if (negative) { + pv = 0 - pv; // the 0 is for Microsoft + } + size_t dc = internal::digit_count(pv); + // by always writing the minus sign, we avoid the branch. + buffer.get()[position] = '-'; + position += negative ? 1 : 0; + char *write_pointer = buffer.get() + position + dc - 1; + + // Process 4 digits per iteration for large numbers + while (pv >= 10000) { + unsigned_type q = pv / 10000; + unsigned_type r = pv % 10000; + unsigned_type r_hi = r / 100; + unsigned_type r_lo = r % 100; + memcpy(write_pointer - 1, &internal::decimal_table[r_lo * 2], 2); + memcpy(write_pointer - 3, &internal::decimal_table[r_hi * 2], 2); + write_pointer -= 4; + pv = q; + } + + // Handle remaining 1-4 digits + while (pv >= 100) { + memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2); + write_pointer -= 2; + pv /= 100; + } + if (pv >= 10) { + *write_pointer-- = char('0' + (pv % 10)); + pv /= 10; + } + *write_pointer = char('0' + pv); + position += dc; + } + } + else SIMDJSON_IF_CONSTEXPR(std::is_floating_point::value) { + constexpr size_t max_number_size = 24; + if (capacity_check(max_number_size)) { + // 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. + 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. + 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 +simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept { + escape_and_append_with_quotes(constevalutil::string_constant::value); +} +#endif + +simdjson_inline void string_builder::append_raw(const char *c) noexcept { + size_t len = std::strlen(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; + } +} +#if SIMDJSON_SUPPORTS_CONCEPTS +// Support for optional types (std::optional, etc.) +template + requires(!require_custom_serialization) +simdjson_inline void string_builder::append(const T &opt) { + if (opt) { + append(*opt); + } else { + append_null(); + } +} + +template + requires(require_custom_serialization) +simdjson_inline void string_builder::append(T &&val) { + serialize(*this, std::forward(val)); +} + +template + requires(std::is_convertible::value || + std::is_same::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 + requires(!std::is_convertible::value && !require_custom_serialization) +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>) { + 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 +string_builder::view() const noexcept { + if (!is_valid) { + return simdjson::OUT_OF_CAPACITY; + } + return std::string_view(buffer.get(), position); +} + +simdjson_inline simdjson_result 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 +simdjson_inline void +string_builder::append_key_value(key_type key, value_type value) noexcept { + static_assert(std::is_same::value || + std::is_convertible::value, + "Unsupported key type"); + escape_and_append_with_quotes(key); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(value); + } +} + +#if SIMDJSON_SUPPORTS_CONCEPTS +template +simdjson_inline void +string_builder::append_key_value(value_type value) noexcept { + escape_and_append_with_quotes(); + append_colon(); + SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + append_null(); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR( + std::is_convertible::value) { + escape_and_append_with_quotes(value); + } + else SIMDJSON_IF_CONSTEXPR(std::is_same::value) { + escape_and_append_with_quotes(value); + } + else { + append(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/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" */ + +#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 struct simd8; +template 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 + +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) { + return vaddv_u8(vcnt_u8(vcreate_u8(input_num))); +} + + +#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 + +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t *result) { +#if SIMDJSON_REGULAR_VISUAL_STUDIO + *result = value1 + value2; + return *result < value1; +#else + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); +#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 + +#if SIMDJSON_REGULAR_VISUAL_STUDIO && SIMDJSON_IS_ARM64 +// __umulh requires intrin.h +#include +#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 + struct simd8; + + // + // Base class of simd8 and simd8, both of which use uint8x16_t internally. + // + template> + 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 operator|(const simd8 other) const { return vorrq_u8(*this, other); } + simdjson_inline simd8 operator&(const simd8 other) const { return vandq_u8(*this, other); } + simdjson_inline simd8 operator^(const simd8 other) const { return veorq_u8(*this, other); } + simdjson_inline simd8 bit_andnot(const simd8 other) const { return vbicq_u8(*this, other); } + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } + simdjson_inline simd8& operator|=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline simd8& operator&=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline simd8& operator^=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast ^ other; return *this_cast; } + + friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return vceqq_u8(lhs, rhs); } + + template + simdjson_inline simd8 prev(const simd8 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: base_u8 { + typedef uint16_t bitmask_t; + typedef uint32_t bitmask2_t; + + static simdjson_inline simd8 splat(bool _value) { return vmovq_n_u8(uint8_t(-(!!_value))); } + + simdjson_inline simd8(const uint8x16_t _value) : base_u8(_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); + } + simdjson_inline bool any() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) != 0; } + }; + + // Unsigned bytes + template<> + struct simd8: base_u8 { + 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(_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 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( + 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 saturating_add(const simd8 other) const { return vqaddq_u8(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return vqsubq_u8(*this, other); } + + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { return vaddq_u8(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return vsubq_u8(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *this; } + simdjson_inline simd8& operator-=(const simd8 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 max_val(const simd8 other) const { return vmaxq_u8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return vminq_u8(*this, other); } + simdjson_inline simd8 operator<=(const simd8 other) const { return vcleq_u8(*this, other); } + simdjson_inline simd8 operator>=(const simd8 other) const { return vcgeq_u8(*this, other); } + simdjson_inline simd8 operator<(const simd8 other) const { return vcltq_u8(*this, other); } + simdjson_inline simd8 operator>(const simd8 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 gt_bits(const simd8 other) const { return simd8(*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 lt_bits(const simd8 other) const { return simd8(*this < other); } + + // Bit-specific operations + simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return (*this & bits).any_bits_set_anywhere(); } + template + simdjson_inline simd8 shr() const { return vshrq_n_u8(*this, N); } + template + simdjson_inline simd8 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 + simdjson_inline simd8 lookup_16(simd8 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 compress(uint16_t mask) would be + // sensible, but the AVX ISA makes this kind of approach difficult. + template + 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(pshufb_combine_table + pop1 * 8)); + uint8x16_t answer = vqtbl1q_u8(pruned, compactmask); + vst1q_u8(reinterpret_cast(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 + 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 + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + + template + simdjson_inline simd8 apply_lookup_16_to(const simd8 original) { + return vqtbl1q_u8(*this, simd8(original)); + } + }; + + // Signed bytes + template<> + struct simd8 { + int8x16_t value; + + static simdjson_inline simd8 splat(int8_t _value) { return vmovq_n_s8(_value); } + static simdjson_inline simd8 zero() { return vdupq_n_s8(0); } + static simdjson_inline simd8 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 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( + 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() const { return vreinterpretq_u8_s8(this->value); } + + // Math + simdjson_inline simd8 operator+(const simd8 other) const { return vaddq_s8(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return vsubq_s8(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *this; } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *this; } + + // Order-sensitive comparisons + simdjson_inline simd8 max_val(const simd8 other) const { return vmaxq_s8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return vminq_s8(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return vcgtq_s8(*this, other); } + simdjson_inline simd8 operator<(const simd8 other) const { return vcltq_s8(*this, other); } + simdjson_inline simd8 operator==(const simd8 other) const { return vceqq_s8(*this, other); } + + template + simdjson_inline simd8 prev(const simd8 prev_chunk) const { + return vextq_s8(prev_chunk, *this, 16 - N); + } + + // Perform a lookup assuming no value is larger than 16 + template + simdjson_inline simd8 lookup_16(simd8 lookup_table) const { + return lookup_table.apply_lookup_16_to(*this); + } + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + + template + simdjson_inline simd8 apply_lookup_16_to(const simd8 original) { + return vqtbl1q_s8(*this, simd8(original)); + } + }; + + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 4, "ARM kernel should use four registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } + + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed + + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::load(ptr+16), simd8::load(ptr+32), simd8::load(ptr+48)} {} + + simdjson_inline void store(T ptr[64]) const { + this->chunks[0].store(ptr+sizeof(simd8)*0); + this->chunks[1].store(ptr+sizeof(simd8)*1); + this->chunks[2].store(ptr+sizeof(simd8)*2); + this->chunks[3].store(ptr+sizeof(simd8)*3); + } + + simdjson_inline simd8 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 mask = simd8::splat(m); + return simd8x64( + 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 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] <= mask, + this->chunks[1] <= mask, + this->chunks[2] <= mask, + this->chunks[3] <= mask + ).to_bitmask(); + } + }; // struct simd8x64 + +} // 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 v0(src); + simd8 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(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 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 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_BUILDER_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; + +} // 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 struct is_builtin_deserializable : std::false_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; + +template +concept is_builtin_deserializable_v = is_builtin_deserializable::value; + +template +concept custom_deserializable = tag_invocable; + +template +concept deserializable = custom_deserializable || is_builtin_deserializable_v || concepts::optional_type; + +template +concept nothrow_custom_deserializable = nothrow_tag_invocable; + +// built-in types are noexcept and if an error happens, the value simply gets ignored and the error is returned. +template +concept nothrow_deserializable = nothrow_custom_deserializable || is_builtin_deserializable_v; + +/// 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 + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(array_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for object + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(object_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for value + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document reference + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + 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 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 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 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 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 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 has_next_field() noexcept; + + /** + * Get the current field's key. + */ + simdjson_warn_unused simdjson_inline simdjson_result field_key() 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 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 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 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 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 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 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 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 get_string(bool allow_replacement) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_int64() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_double() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_bool() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result is_null() noexcept; + simdjson_warn_unused simdjson_inline bool is_negative() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result is_integer() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_number() noexcept; + + simdjson_warn_unused simdjson_inline simdjson_result get_root_string(bool check_trailing, bool allow_replacement) noexcept; + template + 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 get_root_wobbly_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_raw_json_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64_in_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_int64(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_int64_in_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_double(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_double_in_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_bool(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline bool is_root_negative() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result is_root_integer(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_number_type(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_number(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result 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 reset_array() noexcept; + /** + * Restarts an object iteration. + * @returns Whether the object has any fields (returns false for empty). + */ + simdjson_inline simdjson_result 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 parse_null(const uint8_t *json) const noexcept; + simdjson_inline simdjson_result 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 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; + /** @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 : public arm64::implementation_simdjson_result_base { +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 */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include + +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, uint64_t, int64_t, double, bool + * + * You may use get_double(), get_bool(), get_uint64(), get_int64(), + * 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 + simdjson_inline simdjson_result get() +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + #if SIMDJSON_SUPPORTS_CONCEPTS + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else if constexpr (concepts::optional_type) { + using value_type = typename std::remove_cvref_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(out.value()); + } else { + static_assert(!sizeof(T), "The get 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 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, double, or bool."); + static_cast(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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " 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(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 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 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 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 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 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 get_int64_in_string() 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 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 get_double_in_string() noexcept; + + /** + * Cast this JSON value to a string. + * + * The string is guaranteed to be valid UTF-8. + * + * Equivalent to get(). + * + * 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 get_string(bool allow_replacement = false) noexcept; + + /** + * 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 + 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 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 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 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 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 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 + 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 signed 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(). + * + * @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 begin() & noexcept; + /** + * Sentinel representing the end of the array. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result 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 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 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 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 find_field(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ + simdjson_inline simdjson_result 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 find_field_unordered(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result find_field_unordered(const char *key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result 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 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 is_scalar() noexcept; + /** + * Checks whether the value is a string. + * + * @returns true if the type is string + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + */ + simdjson_inline simdjson_result is_string() noexcept; + + /** + * Checks whether the value is a negative number. + * + * @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 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 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 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 raw_json() noexcept; + + /** + * Returns the current location in the document if in bounds. + */ + simdjson_inline simdjson_result 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 at_pointer(std::string_view json_pointer) noexcept; + + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + simdjson_inline simdjson_result at_path(std::string_view at_path) noexcept; + + /** + * Get all values matching the given JSONPath expression with wildcard support. + * Supports wildcard character (*) for arrays or ".*" for objects. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern + */ + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) 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 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; + friend struct simdjson_result; + friend class field; +}; + +} // namespace ondemand +} // namespace arm64 +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public arm64::implementation_simdjson_result_base { +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 get_array() noexcept; + simdjson_inline simdjson_result get_object() noexcept; + + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result is_null() noexcept; + + template simdjson_inline simdjson_result get() noexcept; + + template simdjson_inline error_code get(T &out) noexcept; + +#if SIMDJSON_EXCEPTIONS + template + 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 count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result 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 find_field(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ + simdjson_inline simdjson_result 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 find_field_unordered(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result find_field_unordered(const char *key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result 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 type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + simdjson_inline simdjson_result is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + + /** @copydoc simdjson_inline std::string_view value::raw_json_token() const noexcept */ + simdjson_inline simdjson_result raw_json_token() noexcept; + simdjson_inline simdjson_result raw_json() noexcept; + + /** @copydoc simdjson_inline simdjson_result current_location() noexcept */ + simdjson_inline simdjson_result current_location() noexcept; + /** @copydoc simdjson_inline int32_t current_depth() const noexcept */ + simdjson_inline simdjson_result current_depth() const noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) 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 +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 +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 (`{` `}` `[` `]` `,` `:` `""` `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 value_iterator; + friend class object; + template + friend simdjson_inline void logger::log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta, logger::log_level level, Args&&... args) noexcept; + template + 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 : public arm64::implementation_simdjson_result_base { +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}; + +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; + + /** + * 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 unescape(raw_json_string in, bool allow_replacement) noexcept; + simdjson_inline simdjson_result 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 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 + 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 + 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 : public arm64::implementation_simdjson_result_base { +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 + 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 + 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 &type) noexcept(false); +#endif + +} // namespace ondemand +} // namespace arm64 +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public arm64::implementation_simdjson_result_base { +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 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 unescape_wobbly(json_iterator &iter) const noexcept; + const uint8_t * buf{}; + friend class object; + friend class field; + friend class parser; + friend struct simdjson_result; +}; + +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 : public arm64::implementation_simdjson_result_base { +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 raw() const noexcept; + simdjson_inline char operator[](size_t) const noexcept; + simdjson_inline simdjson_warn_unused simdjson_result unescape(arm64::ondemand::json_iterator &iter, bool allow_replacement) const noexcept; + simdjson_inline simdjson_warn_unused simdjson_result 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 +#include + +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 iterate(padded_string_view json) & noexcept; +#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON + simdjson_warn_unused simdjson_result iterate_allow_incomplete_json(padded_string_view json) & noexcept; +#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const char *json, size_t len, size_t capacity) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const uint8_t *json, size_t len, size_t capacity) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(std::string_view json, size_t capacity) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const std::string &json) & noexcept; + /** @overload simdjson_result 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 iterate(std::string &json) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const simdjson_result &json) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const simdjson_result &json) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result 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 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 10MB, which has been a reasonable sweet spot in our tests. + * @param allow_comma_separated (defaults on false) This allows a mode where the documents are + * separated by commas instead of whitespace. It comes with a performance + * penalty because the entire document is indexed at once (and the document must be + * less than 4 GB), and there is no multithreading. In this mode, the batch_size parameter + * is effectively ignored, as it is set to at least the document size. + * @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 iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(padded_string_view json, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_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 iterate_many(std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @private We do not want to allow implicit conversion from C string to std::string. */ + simdjson_result iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete; + + /** 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 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 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: + friend bool release_parser(); + friend ondemand::parser& get_parser(); + /** Get the thread-local parser instance, allocates it if needed */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_parser_instance(); + /** Get the thread-local parser instance, it might be null */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_threadlocal_parser_if_exists(); + /** @private [for benchmarking access] The implementation to use */ + std::unique_ptr implementation{}; + size_t _capacity{0}; + size_t _max_capacity; + size_t _max_depth{DEFAULT_MAX_DEPTH}; + std::unique_ptr string_buf{}; + +#if SIMDJSON_DEVELOPMENT_CHECKS + std::unique_ptr start_positions{}; +#endif + + friend class json_iterator; + friend class document_stream; +}; + +} // namespace ondemand +} // namespace arm64 +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public arm64::implementation_simdjson_result_base { +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 */ +/* 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. + */ + simdjson_inline simdjson_result begin() noexcept; + /** + * Sentinel representing the end of the array. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result 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 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 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 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 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 at_path(std::string_view json_path) noexcept; + + /** + * Get all values matching the given JSONPath expression with wildcard support. + * Supports wildcard patterns like "[*]" to match all array elements. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern + */ + inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + + /** + * Consumes the array and returns a string_view instance corresponding to the + * array as represented in JSON. It points inside the original document. + */ + simdjson_inline simdjson_result 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 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + 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 + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(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 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 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 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{}; + + friend class value; + friend class document; + friend struct simdjson_result; + friend struct simdjson_result; + friend class array_iterator; +}; + +} // namespace ondemand +} // namespace arm64 +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public arm64::implementation_simdjson_result_base { +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 begin() noexcept; + simdjson_inline simdjson_result end() noexcept; + inline simdjson_result count_elements() & noexcept; + inline simdjson_result is_empty() & noexcept; + inline simdjson_result reset() & noexcept; + simdjson_inline simdjson_result at(size_t index) noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h + + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(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 */ +/* 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; + 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; + + // + // Iterator interface + // + + /** + * Get the current element. + * + * Part of the std::iterator interface. + */ + simdjson_inline simdjson_result + 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}; +#endif + value_iterator iter{}; + + simdjson_inline array_iterator(const value_iterator &iter) noexcept; + + friend class array; + friend class value; + friend struct simdjson_result; +}; + +} // namespace ondemand +} // namespace arm64 +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public arm64::implementation_simdjson_result_base { + using iterator_category = std::input_iterator_tag; + using value_type = simdjson_result; + 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 operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline bool operator==(const simdjson_result &) const noexcept; + simdjson_inline bool operator!=(const simdjson_result &) const noexcept; + simdjson_inline simdjson_result &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 */ +/* 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 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 get_object() & noexcept; + /** + * Cast this JSON value to an unsigned integer. + * + * @returns A signed 64-bit integer. + * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. + */ + simdjson_inline simdjson_result get_uint64() noexcept; + /** + * Cast this JSON value (inside string) to an unsigned integer. + * + * @returns A signed 64-bit integer. + * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. + */ + simdjson_inline simdjson_result 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 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 get_int64_in_string() 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 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 get_double_in_string() noexcept; + /** + * 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 get_string(bool allow_replacement = false) noexcept; + /** + * 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 + 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 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 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 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 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 is_null() noexcept; + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool + * + * You may use get_double(), get_bool(), get_uint64(), get_int64(), + * 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 + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + /** + * @overload template simdjson_result 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 + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::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(*this).get(); + } + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_CONCEPTS + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { + static_assert(!sizeof(T), "The get 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 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, double, or bool."); + static_cast(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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " 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(out); // to get rid of unused errors + return UNINITIALIZED; +#endif // SIMDJSON_SUPPORTS_CONCEPTS + } + + /** @overload template error_code get(T &out) & noexcept */ + template simdjson_deprecated simdjson_inline error_code get(T &out) && noexcept; + +#if SIMDJSON_EXCEPTIONS + /** + * Cast this JSON value to an instance of type T. The programmer is responsible for + * providing an implementation of get 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 + explicit simdjson_inline operator T() & noexcept(false); + template + 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 signed 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. + * + * @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) 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. + */ + 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. + */ + 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 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 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 at(size_t index) & noexcept; + /** + * Begin array iteration. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result begin() & noexcept; + /** + * Sentinel representing the end of the array. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result 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 find_field(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result 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 find_field_unordered(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result 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 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 is_scalar() noexcept; + + /** + * Checks whether the document is a string. + * + * @returns true if the type is string + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + */ + simdjson_inline simdjson_result is_string() noexcept; + + /** + * Checks whether the document is a negative number. + * + * @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 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 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 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 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 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 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 at_path(std::string_view json_path) noexcept; + + /** + * Get all values matching the given JSONPath expression with wildcard support. + * + * Supports wildcard patterns like "$.array[*]" or "$.object.*" to match multiple elements. + * + * This method materializes all matching values into a vector. + * The document will be consumed after this call. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern, or: + * - INVALID_JSON_POINTER if the JSONPath cannot be parsed + * - NO_SUCH_FIELD if a field does not exist + * - INDEX_OUT_OF_BOUNDS if an array index is out of bounds + * - INCORRECT_TYPE if path traversal encounters wrong type + */ + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + + /** + * Consumes the document and returns a string_view instance corresponding to the + * document as represented in JSON. It points inside the original byte array containing + * the JSON document. + */ + simdjson_inline simdjson_result 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 + requires(std::is_class_v && (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 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 get_array() & noexcept; + simdjson_inline simdjson_result get_object() & noexcept; + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result get_value() noexcept; + + simdjson_inline simdjson_result is_null() noexcept; + template + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + template + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::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(*this).get(); + } + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_CONCEPTS + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { + static_assert(!sizeof(T), "The get 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 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, double, or bool."); + static_cast(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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " 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(out); // to get rid of unused errors + return UNINITIALIZED; +#endif // SIMDJSON_SUPPORTS_CONCEPTS + } + + /** @overload template error_code get(T &out) & noexcept */ + template simdjson_inline error_code get(T &out) && noexcept; + simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (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 + explicit simdjson_inline operator T() noexcept(false); + simdjson_inline operator array() & noexcept(false); + simdjson_inline operator 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 raw_json_string() noexcept(false); + simdjson_inline operator bool() noexcept(false); + simdjson_inline operator value() noexcept(false); +#endif + simdjson_inline simdjson_result count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) & noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result end() & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(const char *key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + + simdjson_inline simdjson_result type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + + simdjson_inline simdjson_result current_location() noexcept; + simdjson_inline int32_t current_depth() const noexcept; + simdjson_inline bool is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + simdjson_inline simdjson_result raw_json_token() noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + +private: + document *doc{nullptr}; +}; +} // namespace ondemand +} // namespace arm64 +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public arm64::implementation_simdjson_result_base { +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 get_array() & noexcept; + simdjson_inline simdjson_result get_object() & noexcept; + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result get_value() noexcept; + simdjson_inline simdjson_result is_null() noexcept; + + template simdjson_inline simdjson_result get() & noexcept; + template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept; + + template simdjson_inline error_code get(T &out) & noexcept; + template simdjson_inline error_code get(T &out) && noexcept; +#if SIMDJSON_EXCEPTIONS + + using arm64::implementation_simdjson_result_base::operator*; + using arm64::implementation_simdjson_result_base::operator->; + template ::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); + 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); + simdjson_inline operator arm64::ondemand::value() noexcept(false); +#endif + simdjson_inline simdjson_result count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) & noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result end() & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(const char *key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + simdjson_inline simdjson_result type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + simdjson_inline simdjson_result current_location() noexcept; + simdjson_inline int32_t current_depth() const noexcept; + simdjson_inline bool at_end() const noexcept; + simdjson_inline bool is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + /** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */ + simdjson_inline simdjson_result raw_json_token() noexcept; + + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (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 : public arm64::implementation_simdjson_result_base { +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 get_array() & noexcept; + simdjson_inline simdjson_result get_object() & noexcept; + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result get_value() noexcept; + simdjson_inline simdjson_result is_null() noexcept; + + template simdjson_inline simdjson_result get() & noexcept; + template simdjson_inline simdjson_result get() && noexcept; + + template simdjson_inline error_code get(T &out) & noexcept; + template simdjson_inline error_code get(T &out) && noexcept; +#if SIMDJSON_EXCEPTIONS + template + 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); + simdjson_inline operator arm64::ondemand::value() noexcept(false); +#endif + simdjson_inline simdjson_result count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) & noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result end() & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(const char *key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + simdjson_inline simdjson_result type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + simdjson_inline simdjson_result current_location() noexcept; + simdjson_inline simdjson_result current_depth() const noexcept; + simdjson_inline simdjson_result is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + /** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */ + simdjson_inline simdjson_result raw_json_token() noexcept; + + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (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 +#include +#include +#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(); + * + */ + inline size_t truncated_bytes() const noexcept; + + class iterator { + public: + using value_type = simdjson_result; + using reference = simdjson_result; + 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. + * + * 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) + */ + simdjson_inline document_stream( + ondemand::parser &parser, + const uint8_t *buf, + size_t len, + size_t batch_size, + bool allow_comma_separated + ) 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. */ + inline void next_document() 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; + /** + * 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 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. + */ + 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; + friend struct simdjson::internal::simdjson_result_base; +}; // document_stream + +} // namespace ondemand +} // namespace arm64 +} // namespace simdjson + +namespace simdjson { +template<> +struct simdjson_result : public arm64::implementation_simdjson_result_base { +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 so you can use C++ algorithms that rely on pairs. + */ +class field : public std::pair { +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 unescaped_key(bool allow_replacement = false) 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. 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 + 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; + /** + * 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 start(value_iterator &parent_iter) noexcept; + static simdjson_inline simdjson_result start(const value_iterator &parent_iter, raw_json_string key) noexcept; + friend struct simdjson_result; + friend class object_iterator; +}; + +} // namespace ondemand +} // namespace arm64 +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public arm64::implementation_simdjson_result_base { +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 unescaped_key(bool allow_replacement = false) noexcept; + template + simdjson_inline error_code unescaped_key(string_type &receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result key() noexcept; + simdjson_inline simdjson_result key_raw_json_token() noexcept; + simdjson_inline simdjson_result escaped_key() noexcept; + simdjson_inline simdjson_result value() noexcept; +}; + +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ONDEMAND_FIELD_H +/* end file simdjson/generic/ondemand/field.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 */ +/* 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 { + +/** + * 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 + */ + simdjson_inline simdjson_result begin() noexcept; + simdjson_inline simdjson_result 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"); + * ``` + * 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 find_field(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result 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 find_field_unordered(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) && 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("/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 at_pointer(std::string_view json_pointer) noexcept; + + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + inline simdjson_result at_path(std::string_view json_path) noexcept; + + /** + * Get all values matching the given JSONPath expression with wildcard support. + * Supports wildcard patterns like ".*" to match all object fields. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern + */ + inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + + /** + * Reset the iterator so that we are pointing back at the + * beginning of the object. You should still consume values only once even if you + * 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 reset() & 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 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 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 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + 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 + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(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 + requires(std::is_class_v && (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 start(value_iterator &iter) noexcept; + static simdjson_inline simdjson_result start_root(value_iterator &iter) noexcept; + static simdjson_inline simdjson_result 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{}; + + friend class value; + friend class document; + friend struct simdjson_result; +}; + +} // namespace ondemand +} // namespace arm64 +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public arm64::implementation_simdjson_result_base { +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 begin() noexcept; + simdjson_inline simdjson_result end() noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) && noexcept; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + inline simdjson_result reset() noexcept; + inline simdjson_result is_empty() noexcept; + inline simdjson_result count_fields() & noexcept; + inline simdjson_result raw_json() noexcept; + #if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h + + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(out) ); + } + return SUCCESS; + } + +#if SIMDJSON_STATIC_REFLECTION + // TODO: move this code into object-inl.h + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) noexcept { + if (error()) { return error(); } + return first.extract_into(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; + + // + // Iterator interface + // + + // Reads key and value, yielding them to the user. + // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline simdjson_result 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}; +#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; + friend class object; +}; + +} // namespace ondemand +} // namespace arm64 +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public arm64::implementation_simdjson_result_base { +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 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 &) const noexcept; + // Assumes it's being compared with the end. true if depth >= iter->depth. + simdjson_inline bool operator!=(const simdjson_result &) const noexcept; + // Checks for ']' and ',' + simdjson_inline simdjson_result &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/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 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 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 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 to_json_string(arm64::ondemand::array& x) noexcept; +inline simdjson_result to_json_string(simdjson_result x); +inline simdjson_result to_json_string(simdjson_result x); +inline simdjson_result to_json_string(simdjson_result x); +inline simdjson_result to_json_string(simdjson_result 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 + requires(!std::same_as && + !std::same_as && + !std::same_as && + !std::same_as) +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 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 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&& 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&& 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 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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +#include +#if SIMDJSON_STATIC_REFLECTION +#include +// #include // for std::define_static_string - header not available yet +#endif + +namespace simdjson { + +////////////////////////////// +// Number deserialization +////////////////////////////// + +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + uint64_t x; + SIMDJSON_TRY(val.get_uint64().get(x)); + if (x > (limits::max)()) { + return NUMBER_OUT_OF_RANGE; + } + out = static_cast(x); + return SUCCESS; +} + +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + double x; + SIMDJSON_TRY(val.get_double().get(x)); + out = static_cast(x); + return SUCCESS; +} + +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + 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(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 +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v) { + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + out = T{str}; + return SUCCESS; +} + + +/** + * 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>(). + */ +template +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { + using value_type = typename std::remove_cvref_t::value_type; + static_assert( + deserializable, + "The specified type inside the container must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the container must default constructible."); + arm64::ondemand::array arr; + if constexpr (std::is_same_v, arm64::ondemand::array>) { + arr = val; + } else { + SIMDJSON_TRY(val.get_array().get(arr)); + } + + for (auto v : arr) { + if constexpr (concepts::returns_reference) { + if (auto const err = v.get().get(concepts::emplace_one(out)); + err) { + // If an error occurs, the empty element that we just inserted gets + // removed. We're not using a temp variable because if T is a heavy + // type, we want the valid path to be the fast path and the slow path be + // the path that has errors in it. + if constexpr (requires { out.pop_back(); }) { + static_cast(out.pop_back()); + } + return err; + } + } else { + value_type temp; + if (auto const err = v.get().get(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 +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { + using value_type = typename std::remove_cvref_t::mapped_type; + static_assert( + deserializable, + "The specified value type inside the container must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "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().get(this_value)); + [[maybe_unused]] std::pair 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 +error_code tag_invoke(deserialize_tag, arm64::ondemand::object &obj, T &out) noexcept { + using value_type = typename std::remove_cvref_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 +error_code tag_invoke(deserialize_tag, arm64::ondemand::value &val, T &out) noexcept { + arm64::ondemand::object obj; + SIMDJSON_TRY(val.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} + +template +error_code tag_invoke(deserialize_tag, arm64::ondemand::document &doc, T &out) noexcept { + arm64::ondemand::object obj; + SIMDJSON_TRY(doc.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} + +template +error_code tag_invoke(deserialize_tag, arm64::ondemand::document_reference &doc, T &out) noexcept { + 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. + * + * If constructing T is nothrow, this conversion should be nothrow as well since + * we return MEMALLOC if we're not able to allocate memory instead of throwing + * the error message. + * + * @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 +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::element_type, ValT>) { + using element_type = typename std::remove_cvref_t::element_type; + + // For better error messages, don't use these as constraints on + // the tag_invoke CPO. + static_assert( + deserializable, + "The specified type inside the unique_ptr must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the unique_ptr must default constructible."); + + auto ptr = new (std::nothrow) element_type(); + if (ptr == nullptr) { + return MEMALLOC; + } + SIMDJSON_TRY(val.template get(*ptr)); + out.reset(ptr); + return SUCCESS; +} + +/** + * This CPO (Customization Point Object) will help deserialize into optional types. + */ +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable::value_type, decltype(val)>) { + using value_type = typename std::remove_cvref_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(out.value())); + return SUCCESS; +} + + +#if SIMDJSON_STATIC_REFLECTION + + +template +constexpr bool user_defined_type = (std::is_class_v +&& !std::is_same_v && !std::is_same_v && !concepts::optional_type && +!concepts::appendable_containers); + + +template + requires(user_defined_type && std::is_class_v) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { + arm64::ondemand::object obj; + if constexpr (std::is_same_v, arm64::ondemand::object>) { + obj = val; + } else { + SIMDJSON_TRY(val.get_object().get(obj)); + } + 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)); + if constexpr (concepts::optional_type) { + // for optional members, it's ok if the key is missing + auto error = obj[key].get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + if(error == NO_SUCH_FIELD) { + out.[:mem:].reset(); + continue; + } + return error; + } + } else { + // for non-optional members, the key must be present + SIMDJSON_TRY(obj[key].get(out.[:mem:])); + } + } + }; + return simdjson::SUCCESS; +} + +// Support for enum deserialization - deserialize from string representation using expand approach from P2996R12 +template + requires(std::is_enum_v) +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)); + constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T)); + template for (constexpr auto enum_val : enumerators) { + if (str == std::meta::identifier_of(enum_val)) { + out = [:enum_val:]; + return SUCCESS; + } + }; + + return INCORRECT_TYPE; +#else + // Fallback: deserialize as integer if reflection not available + std::underlying_type_t int_val; + SIMDJSON_TRY(val.get(int_val)); + out = static_cast(int_val); + return SUCCESS; +#endif +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr &out) noexcept { + if (!out) { + out = std::make_unique(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr &out) noexcept { + if (!out) { + out = std::make_shared(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +#endif // SIMDJSON_STATIC_REFLECTION + +//////////////////////////////////////// +// Unique pointers +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + 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 &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + 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 &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_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 &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_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 &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(); + } + int64_t temp; + SIMDJSON_TRY(val.get_int64().get(temp)); + *out = static_cast(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::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::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::started(value_iterator &iter) noexcept { + bool has_value; + SIMDJSON_TRY(iter.started_array().get(has_value)); + return array(iter); +} + +simdjson_inline simdjson_result array::begin() noexcept { +#if SIMDJSON_DEVELOPMENT_CHECKS + if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } +#endif + return array_iterator(iter); +} +simdjson_inline simdjson_result 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 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(starting_point), size_t(final_point - starting_point)); +} + +SIMDJSON_PUSH_DISABLE_WARNINGS +SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING +simdjson_inline simdjson_result array::count_elements() & 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 array after counting the number of elements. + iter.reset_array(); + return count; +} +SIMDJSON_POP_DISABLE_WARNINGS + +simdjson_inline simdjson_result array::is_empty() & noexcept { + bool is_not_empty; + auto error = iter.reset_array().get(is_not_empty); + if(error) { return error; } + return !is_not_empty; +} + +inline simdjson_result array::reset() & noexcept { + return iter.reset_array(); +} + +inline simdjson_result array::at_pointer(std::string_view json_pointer) noexcept { + if (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; } + + // Read the array index + size_t array_index = 0; + size_t i; + for (i = 0; i < json_pointer.length() && json_pointer[i] != '/'; i++) { + uint8_t digit = uint8_t(json_pointer[i] - '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; } + array_index = array_index*10 + digit; + } + + // 0 followed by other digits is invalid + if (i > 1 && json_pointer[0] == '0') { return INVALID_JSON_POINTER; } // "JSON pointer array index has other characters after 0" + + // Empty string is invalid; so is a "/" with no digits before it + if (i == 0) { return INVALID_JSON_POINTER; } // "Empty string in JSON pointer array index" + // 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 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); +} + +inline simdjson_result> array::at_path_with_wildcard(std::string_view json_path) noexcept { + std::vector result; + + 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; + // Wildcard case + if(key=="*"){ + for(auto element: *this){ + + if(element.error()){ + return element.error(); + } + + if(remaining_path.empty()){ + // Use value_unsafe() because we've already checked for errors above. + // The 'element' is a simdjson_result wrapper, and we need to extract + // the underlying value. value_unsafe() is safe here because error() returned false. + result.push_back(std::move(element).value_unsafe()); + + }else{ + auto nested_result = element.at_path_with_wildcard(remaining_path); + + if(nested_result.error()){ + return nested_result.error(); + } + // Same logic as above. + std::vector nested_matches = std::move(nested_result).value_unsafe(); + + result.insert(result.end(), + std::make_move_iterator(nested_matches.begin()), + std::make_move_iterator(nested_matches.end())); + } + } + return result; + }else{ + // Specific index case in which we access the element at the given index + size_t idx=0; + + for(char c:key){ + if(c < '0' || c > '9'){ + return INVALID_JSON_POINTER; + } + idx = idx*10 + (c - '0'); + } + + auto element = at(idx); + + if(element.error()){ + return element.error(); + } + + if(remaining_path.empty()){ + result.push_back(std::move(element).value_unsafe()); + return result; + }else{ + return element.at_path_with_wildcard(remaining_path); + } + } +} + +simdjson_inline simdjson_result array::at(size_t index) noexcept { + 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::simdjson_result( + arm64::ondemand::array &&value +) noexcept + : implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept + : implementation_simdjson_result_base(error) +{ +} + +simdjson_inline simdjson_result simdjson_result::begin() noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() noexcept { + if (error()) { return error(); } + return first.end(); +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::is_empty() & noexcept { + if (error()) { return error(); } + return first.is_empty(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path(json_path); +} +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path_with_wildcard(json_path); +} +simdjson_inline simdjson_result simdjson_result::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} +{} + +simdjson_inline simdjson_result 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::simdjson_result( + arm64::ondemand::array_iterator &&value +) noexcept + : arm64::implementation_simdjson_result_base(std::forward(value)) +{ + first.iter.assert_is_valid(); +} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : arm64::implementation_simdjson_result_base({}, error) +{ +} + +simdjson_inline simdjson_result simdjson_result::operator*() noexcept { + if (error()) { return error(); } + return *first; +} +simdjson_inline bool simdjson_result::operator==(const simdjson_result &other) const noexcept { + if (!first.iter.is_valid()) { return !error(); } + return first == other.first; +} +simdjson_inline bool simdjson_result::operator!=(const simdjson_result &other) const noexcept { + if (!first.iter.is_valid()) { return error(); } + return first != other.first; +} +simdjson_inline simdjson_result &simdjson_result::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::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 */ + +namespace simdjson { +namespace arm64 { +namespace ondemand { + +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 value::get_array() noexcept { + return array::start(iter); +} +simdjson_inline simdjson_result value::get_object() noexcept { + return object::start(iter); +} +simdjson_inline simdjson_result value::start_or_resume_object() noexcept { + if (iter.at_start()) { + return get_object(); + } else { + return object::resume(iter); + } +} + +simdjson_inline simdjson_result value::get_raw_json_string() noexcept { + return iter.get_raw_json_string(); +} +simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { + return iter.get_string(allow_replacement); +} +template +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 value::get_wobbly_string() noexcept { + return iter.get_wobbly_string(); +} +simdjson_inline simdjson_result value::get_double() noexcept { + return iter.get_double(); +} +simdjson_inline simdjson_result value::get_double_in_string() noexcept { + return iter.get_double_in_string(); +} +simdjson_inline simdjson_result value::get_uint64() noexcept { + return iter.get_uint64(); +} +simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { + return iter.get_uint64_in_string(); +} +simdjson_inline simdjson_result value::get_int64() noexcept { + return iter.get_int64(); +} +simdjson_inline simdjson_result value::get_int64_in_string() noexcept { + return iter.get_int64_in_string(); +} +simdjson_inline simdjson_result value::get_bool() noexcept { + return iter.get_bool(); +} +simdjson_inline simdjson_result value::is_null() noexcept { + return iter.is_null(); +} + +template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result 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); } +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(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(bool& out) noexcept { return get_bool().get(out); } + +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline value::operator T() noexcept(false) { + return get(); +} +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 value::begin() & noexcept { + return get_array().begin(); +} +simdjson_inline simdjson_result value::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result value::count_elements() & noexcept { + simdjson_result 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 value::count_fields() & noexcept { + simdjson_result answer; + auto a = get_object(); + answer = a.count_fields(); + iter.move_at_start(); + return answer; +} +simdjson_inline simdjson_result value::at(size_t index) noexcept { + auto a = get_array(); + return a.at(index); +} + +simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result value::find_field(const char *key) noexcept { + return start_or_resume_object().find_field(key); +} + +simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result value::find_field_unordered(const char *key) noexcept { + return start_or_resume_object().find_field_unordered(key); +} + +simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { + return start_or_resume_object()[key]; +} +simdjson_inline simdjson_result value::operator[](const char *key) noexcept { + return start_or_resume_object()[key]; +} + +simdjson_inline simdjson_result value::type() noexcept { + return iter.type(); +} + +simdjson_inline simdjson_result 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 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 value::is_integer() noexcept { + return iter.is_integer(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { + return iter.get_number_type(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { + return iter.get_number(); +} + +simdjson_inline std::string_view value::raw_json_token() noexcept { + return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); +} + +simdjson_inline simdjson_result 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 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::at_pointer(std::string_view json_pointer) noexcept { + 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::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; + } +} + +inline simdjson_result> value::at_path_with_wildcard(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_with_wildcard(json_path); + case json_type::object: + return (*this).get_object().at_path_with_wildcard(json_path); + default: + return INVALID_JSON_POINTER; + } +} + +} // namespace ondemand +} // namespace arm64 +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + arm64::ondemand::value &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base(error) +{ +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + if (error()) { return error(); } + return {}; +} + +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) noexcept { + if (error()) { return error(); } + return first.find_field(key); +} + +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} + +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) noexcept { + if (error()) { return error(); } + return first[key]; +} + +simdjson_inline simdjson_result simdjson_result::get_array() noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} + +template<> simdjson_inline error_code simdjson_result::get(arm64::ondemand::value &out) noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} + +template simdjson_inline simdjson_result simdjson_result::get() noexcept { + if (error()) { return error(); } + return first.get(); +} +template simdjson_inline error_code simdjson_result::get(T &out) noexcept { + if (error()) { return error(); } + return first.get(out); +} + +template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { + if (error()) { return error(); } + return std::move(first); +} + +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline simdjson_result::operator T() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first.get(); +} +simdjson_inline simdjson_result::operator arm64::ondemand::array() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator arm64::ondemand::object() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator arm64::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { + if (error()) { return error(); } + return first.raw_json(); +} + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { + if (error()) { return error(); } + return first.current_depth(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer( + std::string_view json_pointer) noexcept { + if (error()) { + return error(); + } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + +inline simdjson_result> simdjson_result::at_path_with_wildcard( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path_with_wildcard(json_path); +} + +} // 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 */ + +namespace simdjson { +namespace arm64 { +namespace ondemand { + +simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept + : iter{std::forward(_iter)} +{ + logger::log_start_value(iter, "document"); +} + +simdjson_inline document document::start(json_iterator &&iter) noexcept { + return document(std::forward(iter)); +} + +inline void document::rewind() noexcept { + iter.rewind(); +} + +inline std::string document::to_debug_string() noexcept { + return iter.to_string(); +} + +inline simdjson_result 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 document::start_or_resume_object() noexcept { + if (iter.at_root()) { + return get_object(); + } else { + return object::resume(resume_value_iterator()); + } +} +simdjson_inline simdjson_result document::get_value() noexcept { + // Make sure we start any arrays or objects before returning, so that start_root_() + // 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 document::get_array() & noexcept { + auto value = get_root_value_iterator(); + return array::start_root(value); +} +simdjson_inline simdjson_result 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 document::get_uint64() noexcept { + return get_root_value_iterator().get_root_uint64(true); +} +simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { + return get_root_value_iterator().get_root_uint64_in_string(true); +} +simdjson_inline simdjson_result document::get_int64() noexcept { + return get_root_value_iterator().get_root_int64(true); +} +simdjson_inline simdjson_result document::get_int64_in_string() noexcept { + return get_root_value_iterator().get_root_int64_in_string(true); +} +simdjson_inline simdjson_result document::get_double() noexcept { + return get_root_value_iterator().get_root_double(true); +} +simdjson_inline simdjson_result document::get_double_in_string() noexcept { + return get_root_value_iterator().get_root_double_in_string(true); +} +simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { + return get_root_value_iterator().get_root_string(true, allow_replacement); +} +template +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 document::get_wobbly_string() noexcept { + return get_root_value_iterator().get_root_wobbly_string(true); +} +simdjson_inline simdjson_result document::get_raw_json_string() noexcept { + return get_root_value_iterator().get_root_raw_json_string(true); +} +simdjson_inline simdjson_result document::get_bool() noexcept { + return get_root_value_iterator().get_root_bool(true); +} +simdjson_inline simdjson_result document::is_null() noexcept { + return get_root_value_iterator().is_root_null(true); +} + +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result 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); } +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(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(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 document::get() && noexcept { return get_raw_json_string(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_value(); } + +#if SIMDJSON_EXCEPTIONS +template +simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } +template +simdjson_inline document::operator T() & noexcept(false) { return get(); } +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 document::count_elements() & noexcept { + auto a = get_array(); + simdjson_result 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 document::count_fields() & noexcept { + auto a = get_object(); + simdjson_result 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 document::at(size_t index) & noexcept { + auto a = get_array(); + return a.at(index); +} +simdjson_inline simdjson_result document::begin() & noexcept { + return get_array().begin(); +} +simdjson_inline simdjson_result document::end() & noexcept { + return {}; +} + +simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field(const char *key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::find_field_unordered(const char *key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { + return start_or_resume_object()[key]; +} +simdjson_inline simdjson_result 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 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(starting_point), size_t(final_point - starting_point)); +} + +simdjson_inline simdjson_result document::type() noexcept { + return get_root_value_iterator().type(); +} + +simdjson_inline simdjson_result 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 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 document::is_integer() noexcept { + return get_root_value_iterator().is_root_integer(true); +} + +simdjson_inline simdjson_result document::get_number_type() noexcept { + return get_root_value_iterator().get_root_number_type(true); +} + +simdjson_inline simdjson_result document::get_number() noexcept { + return get_root_value_iterator().get_root_number(true); +} + + +simdjson_inline simdjson_result document::raw_json_token() noexcept { + auto _iter = get_root_value_iterator(); + return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); +} + +simdjson_inline simdjson_result 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 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; + } +} + +simdjson_inline simdjson_result> document::at_path_with_wildcard(std::string_view json_path) noexcept { + rewind(); // Rewind the document each time 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().at_path_with_wildcard(json_path); + case json_type::object: + return (*this).get_object().at_path_with_wildcard(json_path); + default: + return INVALID_JSON_POINTER; + } +} + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (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) { + // 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:])); + } + } + } + }; - } // unnamed namespace - } // namespace westmere + return SUCCESS; +} + +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +} // namespace ondemand +} // namespace arm64 +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + arm64::ondemand::document &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base( + error + ) +{ +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { + if (error()) { return error(); } + return first.get_value(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} + +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} + +template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; +template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first); +} +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(arm64::ondemand::document &out) & noexcept = delete; +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(arm64::ondemand::document &out) && noexcept { + if (error()) { return error(); } + out = std::forward(first); + return SUCCESS; +} + +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} + +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} + +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} + +simdjson_inline bool simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} + +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} + +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} + +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} + + +#if SIMDJSON_EXCEPTIONS +template ::value == false>::type> +simdjson_inline simdjson_result::operator T() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator arm64::ondemand::array() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator arm64::ondemand::object() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator arm64::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator arm64::ondemand::value() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline bool simdjson_result::at_end() const noexcept { + if (error()) { return error(); } + return first.at_end(); +} + + +simdjson_inline int32_t simdjson_result::current_depth() const noexcept { + if (error()) { return error(); } + return first.current_depth(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path(json_path); +} + +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path_with_wildcard(json_path); +} + +#if SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(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 document_reference::get_array() & noexcept { return doc->get_array(); } +simdjson_inline simdjson_result 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 document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } +simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } +simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } +template +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 document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } +simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } +simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } +simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } +simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline document_reference::operator T() noexcept(false) { return get(); } +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 document_reference::count_elements() & noexcept { return doc->count_elements(); } +simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } +simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } +simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } +simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } +simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } +simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } +simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } +simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } +simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } +simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } +simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } +simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } +simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } +simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } +simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } +simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } +simdjson_inline simdjson_result> document_reference::at_path_with_wildcard(std::string_view json_path) noexcept { return doc->at_path_with_wildcard(json_path); } +simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} +simdjson_inline document_reference::operator document&() const noexcept { return *doc; } +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code document_reference::extract_into(T& out) & noexcept { + return doc->extract_into(out); +} +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION +} // namespace ondemand +} // namespace arm64 +} // namespace simdjson + + + +namespace simdjson { +simdjson_inline simdjson_result::simdjson_result(arm64::ondemand::document_reference value, error_code error) + noexcept : implementation_simdjson_result_base(std::forward(value), error) {} + + +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { + if (error()) { return error(); } + return first.get_value(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} +template <> +simdjson_warn_unused simdjson_inline error_code simdjson_result::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::get(arm64::ondemand::document_reference &out) && noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline simdjson_result::operator T() noexcept(false) { + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + if (error()) { throw simdjson_error(error()); } + return first.get(); +} +simdjson_inline simdjson_result::operator arm64::ondemand::array() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator arm64::ondemand::object() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator arm64::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator arm64::ondemand::value() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path_with_wildcard(json_path); +} +#if SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(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 +#include + +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 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 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 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 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 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 +) 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}, + 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}, + allow_comma_separated{false}, + 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 { + 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 document_stream::iterator::operator*() noexcept { + return simdjson_result(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; } + doc_index = batch_start; + 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 + 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(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; + } + } +} + +inline void document_stream::next_document() noexcept { + // 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(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; + if (remaining <= batch_size) { + return p.implementation->stage1(&buf[_batch_start], remaining, stage1_mode::streaming_final); + } else { + return p.implementation->stage1(&buf[_batch_start], batch_size, stage1_mode::streaming_partial); + } +} + +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 { + auto depth = stream->doc.iter.depth(); + auto cur_struct_index = stream->doc.iter._root - stream->parser->implementation->structural_indexes.get(); + + // If at root, process the first token to determine if scalar value + if (stream->doc.iter.at_root()) { + 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 + // TODO: We could remove trailing whitespaces + // This returns a string spanning from start of value to the beginning of the next document (excluded) + { + auto next_index = 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(stream->buf) + current_index(); + while(svlen > 1 && (std::isspace(start[svlen-1]) || start[svlen-1] == '\0')) { + svlen--; + } + return std::string_view(start, svlen); + } + } + cur_struct_index++; + } + + while (cur_struct_index <= static_cast(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(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::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base(error) +{ +} +simdjson_inline simdjson_result::simdjson_result( + arm64::ondemand::document_stream &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(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() {} + +simdjson_inline field::field(raw_json_string key, ondemand::value &&value) noexcept + : std::pair(key, std::forward(value)) +{ +} + +simdjson_inline simdjson_result 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::start(const value_iterator &parent_iter, raw_json_string key) noexcept { + return field(key, parent_iter.child()); +} + +simdjson_inline simdjson_warn_unused simdjson_result 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 answer = first.unescape(second.iter.json_iter(), allow_replacement); + first.consume(); + return answer; +} + +template +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) ); + 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(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(first.buf), end_quote - first.buf); +} + +simdjson_inline value &field::value() & noexcept { + return second; +} + +simdjson_inline value field::value() && noexcept { + return std::forward(*this).second; +} + +} // namespace ondemand +} // namespace arm64 +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + arm64::ondemand::field &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base(error) +{ +} + +simdjson_inline simdjson_result simdjson_result::key() noexcept { + if (error()) { return error(); } + return first.key(); +} + +simdjson_inline simdjson_result simdjson_result::key_raw_json_token() noexcept { + if (error()) { return error(); } + return first.key_raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::escaped_key() noexcept { + if (error()) { return error(); } + return first.escaped_key(); +} + +simdjson_inline simdjson_result simdjson_result::unescaped_key(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.unescaped_key(allow_replacement); +} + +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::unescaped_key(string_type &receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.unescaped_key(receiver, allow_replacement); +} + +simdjson_inline simdjson_result simdjson_result::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(other.token)), + parser{other.parser}, + _string_buf_loc{other._string_buf_loc}, + error{other.error}, + _depth{other._depth}, + _root{other._root}, + _streaming{other._streaming} +{ + 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; + 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} + +{ + 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 '"': + if(*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(TAPE_ERROR, "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; +} + +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(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 json_iterator::current_location() const noexcept { + if (!is_alive()) { // Unrecoverable error + if (!at_root()) { + return reinterpret_cast(token.peek(-1)); + } else { + return reinterpret_cast(token.peek()); + } + } + if (at_end()) { + return OUT_OF_BOUNDS; + } + return reinterpret_cast(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 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 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::simdjson_result(arm64::ondemand::json_iterator &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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; + default: SIMDJSON_UNREACHABLE(); + } + return out; +} + +#if SIMDJSON_EXCEPTIONS +inline std::ostream& operator<<(std::ostream& out, simdjson_result &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::simdjson_result(arm64::ondemand::json_type &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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 +#include + +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 +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_s); + if (size <= 0) return std::string(); + std::unique_ptr 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 +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)...); +} + +template +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)...); + 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): #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 */ +/* amalgamation skipped (editor-only): #endif */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace arm64 { +namespace ondemand { + +simdjson_inline simdjson_result 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} +simdjson_inline simdjson_result 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} +simdjson_inline simdjson_result object::operator[](const std::string_view key) & noexcept { + return find_field_unordered(key); +} +simdjson_inline simdjson_result object::operator[](const std::string_view key) && noexcept { + return std::forward(*this).find_field_unordered(key); +} +simdjson_inline simdjson_result object::find_field(const std::string_view key) & noexcept { + 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} +simdjson_inline simdjson_result object::find_field(const std::string_view key) && noexcept { + 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} + +simdjson_inline simdjson_result object::start(value_iterator &iter) noexcept { + SIMDJSON_TRY( iter.start_object().error() ); + return object(iter); +} +simdjson_inline simdjson_result 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 object::raw_json() noexcept { + 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(starting_point), size_t(final_point - starting_point)); +} + +simdjson_inline simdjson_result 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::begin() noexcept { +#if SIMDJSON_DEVELOPMENT_CHECKS + if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } +#endif + return object_iterator(iter); +} +simdjson_inline simdjson_result object::end() noexcept { + return object_iterator(iter); +} + +inline simdjson_result object::at_pointer(std::string_view json_pointer) noexcept { + if (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 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 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); +} + +inline simdjson_result> object::at_path_with_wildcard(std::string_view json_path) noexcept { + std::vector result; + + 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; + // 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()) { + result.push_back(std::move(val)); + } else { + auto nested_result = val.at_path_with_wildcard(remaining_path); + + if (nested_result.error()) { + return nested_result.error(); + } + // Extract and append all nested matches to our result + std::vector nested_vec; + SIMDJSON_TRY(std::move(nested_result).get(nested_vec)); + + result.insert(result.end(), + std::make_move_iterator(nested_vec.begin()), + std::make_move_iterator(nested_vec.end())); + } + } + return result; + } else { + value val; + SIMDJSON_TRY(find_field(key).get(val)); + + if (remaining_path.empty()) { + result.push_back(std::move(val)); + return result; + } else { + return val.at_path_with_wildcard(remaining_path); + } + } +} + +simdjson_inline simdjson_result 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 object::is_empty() & noexcept { + 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 object::reset() & noexcept { + return iter.reset_object(); +} + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (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) { + // 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::simdjson_result(arm64::ondemand::object &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(error) {} + +simdjson_inline simdjson_result simdjson_result::begin() noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() noexcept { + if (error()) { return error(); } + return first.end(); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) && noexcept { + if (error()) { return error(); } + return std::forward(first).find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) && noexcept { + if (error()) { return error(); } + return std::forward(first)[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) && noexcept { + if (error()) { return error(); } + return std::forward(first).find_field(key); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path_with_wildcard(json_path); +} + +inline simdjson_result simdjson_result::reset() noexcept { + if (error()) { return error(); } + return first.reset(); +} + +inline simdjson_result simdjson_result::is_empty() noexcept { + if (error()) { return error(); } + return first.is_empty(); +} + +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} + +simdjson_inline simdjson_result simdjson_result::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} +{} + +simdjson_inline simdjson_result 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::simdjson_result( + arm64::ondemand::object_iterator &&value +) noexcept + : implementation_simdjson_result_base(std::forward(value)) +{ + first.iter.assert_is_valid(); +} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base({}, error) +{ +} + +simdjson_inline simdjson_result simdjson_result::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::operator==(const simdjson_result &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::operator!=(const simdjson_result &other) const noexcept { + if (!first.iter.is_valid()) { return error(); } + return first != other.first; +} +// Checks for ']' and ',' +simdjson_inline simdjson_result &simdjson_result::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/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; + 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 parser::iterate(padded_string_view json) & noexcept { + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } + + json.remove_utf8_bom(); + + // 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(json.data()), json.length(), stage1_mode::regular) ); + return document::start({ reinterpret_cast(json.data()), this }); +} + +#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept { + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } + + json.remove_utf8_bom(); + + // 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(json.data()), json.length(), stage1_mode::regular); + if (err) { + if (err != UNCLOSED_STRING) + return err; + } + return document::start({ reinterpret_cast(json.data()), this, true }); +} +#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON + +simdjson_warn_unused simdjson_inline simdjson_result 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 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 parser::iterate(std::string_view json, size_t allocated) & noexcept { + return iterate(padded_string_view(json, allocated)); +} + +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(std::string &json) & noexcept { + return iterate(pad_with_reserve(json)); +} + +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const std::string &json) & noexcept { + return iterate(padded_string_view(json)); +} + +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const simdjson_result &result) & noexcept { + // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 parser::iterate(const simdjson_result &result) & noexcept { + // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 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(json.data()), json.length(), stage1_mode::regular) ); + return json_iterator(reinterpret_cast(json.data()), this); +} + +inline simdjson_result 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; + } + if(allow_comma_separated && batch_size < len) { batch_size = len; } + return document_stream(*this, buf, len, batch_size, allow_comma_separated); +} + +inline simdjson_result 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(buf), len, batch_size, allow_comma_separated); +} +inline simdjson_result 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 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 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 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_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 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(dst), end-dst); + dst = end; + return result; +} + +simdjson_inline simdjson_warn_unused simdjson_result 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(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 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& parser::get_parser_instance() { + std::unique_ptr& 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& 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 parser_instance = nullptr; + return parser_instance; +} + + +} // namespace ondemand +} // namespace arm64 +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result(arm64::ondemand::parser &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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(buf); +} + +simdjson_inline char raw_json_string::operator[](size_t i) const noexcept { + return reinterpret_cast(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()); + } + const char * r{raw()}; + size_t pos{0}; + for(;pos < target.size();pos++) { + if(r[pos] != target[pos]) { return false; } + } + if(r[pos] != '"') { return false; } + return true; +} + +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 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 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::simdjson_result(arm64::ondemand::raw_json_string &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(error) {} + +simdjson_inline simdjson_result simdjson_result::raw() const noexcept { + if (error()) { return error(); } + return first.raw(); +} +simdjson_inline char simdjson_result::operator[](size_t i) const noexcept { + if (error()) { return error(); } + return first[i]; +} +simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::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 simdjson_result::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::simdjson_result(arm64::ondemand::token_iterator &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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 { + +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 value_iterator::start_object() noexcept { + SIMDJSON_TRY( start_container('{', "Not an object", "object") ); + return started_object(); +} + +simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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 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 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_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 value_iterator::start_array() noexcept { + SIMDJSON_TRY( start_container('[', "Not an array", "array") ); + return started_array(); +} + +simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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(!not_true); +} +simdjson_warn_unused simdjson_inline simdjson_result 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 value_iterator::get_string(bool allow_replacement) noexcept { + return get_raw_json_string().unescape(json_iter(), allow_replacement); +} +template +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; } + 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 value_iterator::get_wobbly_string() noexcept { + return get_raw_json_string().unescape_wobbly(json_iter()); +} +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_raw_json_string() noexcept { + auto json = peek_scalar("string"); + if (*json != '"') { return incorrect_type_error("Not a string"); } + advance_scalar("string"); + return raw_json_string(json+1); +} +simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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 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 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 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 value_iterator::is_integer() noexcept { + return numberparsing::is_integer(peek_non_root_scalar("integer")); +} +simdjson_inline simdjson_result value_iterator::get_number_type() noexcept { + return numberparsing::get_number_type(peek_non_root_scalar("integer")); +} +simdjson_inline simdjson_result 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 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 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.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 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.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 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 +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; } + 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 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 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; } + advance_scalar("string"); + return raw_json_string(json+1); +} +simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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.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 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.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 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 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 simdjson_result value_iterator::reset_array() noexcept { + if(error()) { return error(); } + move_at_container_start(); + return started_array(); +} + +simdjson_inline simdjson_result 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 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::simdjson_result(arm64::ondemand::value_iterator &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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 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 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 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 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 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 to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result 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 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 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&& x) { + if (x.error()) { throw simdjson::simdjson_error(x.error()); } + return (out << x.value()); +} +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& 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 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 emails; }; + * + * std::string email; + * path_accessor::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 +#include +#include + +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 +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 +consteval auto make_field_step(const char (&name)[N]) { + return path_step(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 +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 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 +struct path_accessor { + using value = ::simdjson::arm64::ondemand::value; + + static constexpr auto parser = json_path_parser(); + 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 + static inline simdjson_result access(DocOrValue& doc_or_val) noexcept { + // Validate path at compile time if T is a struct + if constexpr (std::is_class_v) { + 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(doc_or_val.get_value()); + } + + // Extract value at path directly into target with compile-time type validation + // Example: std::string name; path_accessor::extract_field(doc, name); + template + static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept { + static_assert(std::is_class_v, "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(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 + static consteval std::enable_if_t, 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 + static inline error_code extract_with_reflection(simdjson_result 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(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(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_value, target_ref); + } + } + } + // Skip unexpected characters and continue + else { + return extract_with_reflection(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 + static inline simdjson_result access_impl(simdjson_result 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_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_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_value); + } + } else { + return access_impl(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(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 + 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 + 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 + static consteval bool is_container_type() { + using BaseType = std::remove_cvref_t; + if constexpr (requires { typename BaseType::value_type; }) { + return true; + } + if constexpr (std::is_array_v) { + return true; + } + return false; + } + + // Extract element type from non-reflected container + template + using extract_element_type = std::conditional_t< + requires { typename std::remove_cvref_t::value_type; }, + typename std::remove_cvref_t::value_type, + std::conditional_t< + std::is_array_v>, + std::remove_extent_t>, + void + > + >; + + // Validate path matches struct definition + static consteval bool validate_path() { + if constexpr (!std::is_class_v) { + 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 +inline simdjson_result<::simdjson::arm64::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = path_accessor; + return accessor::access(doc_or_val); +} + +// Overload without type parameter (no validation) +template +inline simdjson_result<::simdjson::arm64::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = path_accessor; + return accessor::access(doc_or_val); +} + +// ============================================================================ +// JSON Pointer Compile-Time Support (RFC 6901) +// ============================================================================ + +// JSON Pointer parser: /field/0/nested (slash-separated) +template +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 +struct pointer_accessor { + using parser = json_pointer_parser; + 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) { + 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::is_array_like_reflected(current_type)) { + return false; + } + current_type = path_accessor::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 + static inline simdjson_result access_impl(simdjson_result 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(next_value); + } else { + auto obj = current.get_object().value_unsafe(); + auto next_value = obj.find_field_unordered(token); + return access_impl(next_value); + } + } + } + + // Access JSON value at pointer + template + static inline simdjson_result access(DocOrValue& doc_or_val) noexcept { + if constexpr (std::is_class_v) { + 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 current = doc_or_val.get_value(); + return access_impl<0>(current); + } + + // Extract value at pointer directly into target with type validation + template + static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept { + static_assert(std::is_class_v, "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 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 + static consteval std::enable_if_t, 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::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 +inline simdjson_result<::simdjson::arm64::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = pointer_accessor; + return accessor::access(doc_or_val); +} + +// Overload without type parameter (no validation) +template +inline simdjson_result<::simdjson::arm64::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = pointer_accessor; + 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 +} + +} // 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 + +#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(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_BUILDER_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; + +} // 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 struct is_builtin_deserializable : std::false_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; + +template +concept is_builtin_deserializable_v = is_builtin_deserializable::value; + +template +concept custom_deserializable = tag_invocable; + +template +concept deserializable = custom_deserializable || is_builtin_deserializable_v || concepts::optional_type; + +template +concept nothrow_custom_deserializable = nothrow_tag_invocable; + +// built-in types are noexcept and if an error happens, the value simply gets ignored and the error is returned. +template +concept nothrow_deserializable = nothrow_custom_deserializable || is_builtin_deserializable_v; + +/// 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 + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(array_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for object + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(object_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for value + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document reference + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + 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 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 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 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 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 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 has_next_field() noexcept; + + /** + * Get the current field's key. + */ + simdjson_warn_unused simdjson_inline simdjson_result field_key() 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 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 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 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 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 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 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 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 get_string(bool allow_replacement) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_int64() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_double() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_bool() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result is_null() noexcept; + simdjson_warn_unused simdjson_inline bool is_negative() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result is_integer() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_number() noexcept; + + simdjson_warn_unused simdjson_inline simdjson_result get_root_string(bool check_trailing, bool allow_replacement) noexcept; + template + 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 get_root_wobbly_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_raw_json_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64_in_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_int64(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_int64_in_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_double(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_double_in_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_bool(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline bool is_root_negative() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result is_root_integer(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_number_type(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_number(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result 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 reset_array() noexcept; + /** + * Restarts an object iteration. + * @returns Whether the object has any fields (returns false for empty). + */ + simdjson_inline simdjson_result 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 parse_null(const uint8_t *json) const noexcept; + simdjson_inline simdjson_result 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 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; + /** @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 : public fallback::implementation_simdjson_result_base { +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 */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include + +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, uint64_t, int64_t, double, bool + * + * You may use get_double(), get_bool(), get_uint64(), get_int64(), + * 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 + simdjson_inline simdjson_result get() +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + #if SIMDJSON_SUPPORTS_CONCEPTS + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else if constexpr (concepts::optional_type) { + using value_type = typename std::remove_cvref_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(out.value()); + } else { + static_assert(!sizeof(T), "The get 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 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, double, or bool."); + static_cast(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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " 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(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 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 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 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 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 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 get_int64_in_string() 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 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 get_double_in_string() noexcept; + + /** + * Cast this JSON value to a string. + * + * The string is guaranteed to be valid UTF-8. + * + * Equivalent to get(). + * + * 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 get_string(bool allow_replacement = false) noexcept; + + /** + * 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 + 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 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 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 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 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 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 + 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 signed 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(). + * + * @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 begin() & noexcept; + /** + * Sentinel representing the end of the array. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result 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 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 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 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 find_field(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ + simdjson_inline simdjson_result 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 find_field_unordered(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result find_field_unordered(const char *key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result 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 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 is_scalar() noexcept; + /** + * Checks whether the value is a string. + * + * @returns true if the type is string + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + */ + simdjson_inline simdjson_result is_string() noexcept; + + /** + * Checks whether the value is a negative number. + * + * @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 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 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 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 raw_json() noexcept; + + /** + * Returns the current location in the document if in bounds. + */ + simdjson_inline simdjson_result 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 at_pointer(std::string_view json_pointer) noexcept; + + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + simdjson_inline simdjson_result at_path(std::string_view at_path) noexcept; + + /** + * Get all values matching the given JSONPath expression with wildcard support. + * Supports wildcard character (*) for arrays or ".*" for objects. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern + */ + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) 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 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; + friend struct simdjson_result; + friend class field; +}; + +} // namespace ondemand +} // namespace fallback +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public fallback::implementation_simdjson_result_base { +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 get_array() noexcept; + simdjson_inline simdjson_result get_object() noexcept; + + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result is_null() noexcept; + + template simdjson_inline simdjson_result get() noexcept; + + template simdjson_inline error_code get(T &out) noexcept; + +#if SIMDJSON_EXCEPTIONS + template + 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 count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result 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 find_field(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ + simdjson_inline simdjson_result 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 find_field_unordered(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result find_field_unordered(const char *key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result 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 type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + simdjson_inline simdjson_result is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + + /** @copydoc simdjson_inline std::string_view value::raw_json_token() const noexcept */ + simdjson_inline simdjson_result raw_json_token() noexcept; + simdjson_inline simdjson_result raw_json() noexcept; + + /** @copydoc simdjson_inline simdjson_result current_location() noexcept */ + simdjson_inline simdjson_result current_location() noexcept; + /** @copydoc simdjson_inline int32_t current_depth() const noexcept */ + simdjson_inline simdjson_result current_depth() const noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) 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 +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 +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 (`{` `}` `[` `]` `,` `:` `""` `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 value_iterator; + friend class object; + template + friend simdjson_inline void logger::log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta, logger::log_level level, Args&&... args) noexcept; + template + 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 : public fallback::implementation_simdjson_result_base { +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}; + +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; + + /** + * 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 unescape(raw_json_string in, bool allow_replacement) noexcept; + simdjson_inline simdjson_result 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 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 + 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 + 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 : public fallback::implementation_simdjson_result_base { +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 + 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 + 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 &type) noexcept(false); +#endif + +} // namespace ondemand +} // namespace fallback +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public fallback::implementation_simdjson_result_base { +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 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 unescape_wobbly(json_iterator &iter) const noexcept; + const uint8_t * buf{}; + friend class object; + friend class field; + friend class parser; + friend struct simdjson_result; +}; + +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 : public fallback::implementation_simdjson_result_base { +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 raw() const noexcept; + simdjson_inline char operator[](size_t) const noexcept; + simdjson_inline simdjson_warn_unused simdjson_result unescape(fallback::ondemand::json_iterator &iter, bool allow_replacement) const noexcept; + simdjson_inline simdjson_warn_unused simdjson_result 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 +#include + +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 iterate(padded_string_view json) & noexcept; +#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON + simdjson_warn_unused simdjson_result iterate_allow_incomplete_json(padded_string_view json) & noexcept; +#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const char *json, size_t len, size_t capacity) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const uint8_t *json, size_t len, size_t capacity) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(std::string_view json, size_t capacity) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const std::string &json) & noexcept; + /** @overload simdjson_result 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 iterate(std::string &json) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const simdjson_result &json) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const simdjson_result &json) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result 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 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 10MB, which has been a reasonable sweet spot in our tests. + * @param allow_comma_separated (defaults on false) This allows a mode where the documents are + * separated by commas instead of whitespace. It comes with a performance + * penalty because the entire document is indexed at once (and the document must be + * less than 4 GB), and there is no multithreading. In this mode, the batch_size parameter + * is effectively ignored, as it is set to at least the document size. + * @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 iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(padded_string_view json, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_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 iterate_many(std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @private We do not want to allow implicit conversion from C string to std::string. */ + simdjson_result iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete; + + /** 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 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 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: + friend bool release_parser(); + friend ondemand::parser& get_parser(); + /** Get the thread-local parser instance, allocates it if needed */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_parser_instance(); + /** Get the thread-local parser instance, it might be null */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_threadlocal_parser_if_exists(); + /** @private [for benchmarking access] The implementation to use */ + std::unique_ptr implementation{}; + size_t _capacity{0}; + size_t _max_capacity; + size_t _max_depth{DEFAULT_MAX_DEPTH}; + std::unique_ptr string_buf{}; + +#if SIMDJSON_DEVELOPMENT_CHECKS + std::unique_ptr start_positions{}; +#endif + + friend class json_iterator; + friend class document_stream; +}; + +} // namespace ondemand +} // namespace fallback +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public fallback::implementation_simdjson_result_base { +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 */ +/* 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. + */ + simdjson_inline simdjson_result begin() noexcept; + /** + * Sentinel representing the end of the array. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result 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 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 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 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 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 at_path(std::string_view json_path) noexcept; + + /** + * Get all values matching the given JSONPath expression with wildcard support. + * Supports wildcard patterns like "[*]" to match all array elements. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern + */ + inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + + /** + * Consumes the array and returns a string_view instance corresponding to the + * array as represented in JSON. It points inside the original document. + */ + simdjson_inline simdjson_result 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 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + 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 + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(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 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 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 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{}; + + friend class value; + friend class document; + friend struct simdjson_result; + friend struct simdjson_result; + friend class array_iterator; +}; + +} // namespace ondemand +} // namespace fallback +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public fallback::implementation_simdjson_result_base { +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 begin() noexcept; + simdjson_inline simdjson_result end() noexcept; + inline simdjson_result count_elements() & noexcept; + inline simdjson_result is_empty() & noexcept; + inline simdjson_result reset() & noexcept; + simdjson_inline simdjson_result at(size_t index) noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h + + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(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 */ +/* 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; + 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; + + // + // Iterator interface + // + + /** + * Get the current element. + * + * Part of the std::iterator interface. + */ + simdjson_inline simdjson_result + 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}; +#endif + value_iterator iter{}; + + simdjson_inline array_iterator(const value_iterator &iter) noexcept; + + friend class array; + friend class value; + friend struct simdjson_result; +}; + +} // namespace ondemand +} // namespace fallback +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public fallback::implementation_simdjson_result_base { + using iterator_category = std::input_iterator_tag; + using value_type = simdjson_result; + 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 operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline bool operator==(const simdjson_result &) const noexcept; + simdjson_inline bool operator!=(const simdjson_result &) const noexcept; + simdjson_inline simdjson_result &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 */ +/* 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 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 get_object() & noexcept; + /** + * Cast this JSON value to an unsigned integer. + * + * @returns A signed 64-bit integer. + * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. + */ + simdjson_inline simdjson_result get_uint64() noexcept; + /** + * Cast this JSON value (inside string) to an unsigned integer. + * + * @returns A signed 64-bit integer. + * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. + */ + simdjson_inline simdjson_result 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 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 get_int64_in_string() 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 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 get_double_in_string() noexcept; + /** + * 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 get_string(bool allow_replacement = false) noexcept; + /** + * 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 + 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 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 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 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 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 is_null() noexcept; + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool + * + * You may use get_double(), get_bool(), get_uint64(), get_int64(), + * 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 + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + /** + * @overload template simdjson_result 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 + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::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(*this).get(); + } + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_CONCEPTS + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { + static_assert(!sizeof(T), "The get 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 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, double, or bool."); + static_cast(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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " 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(out); // to get rid of unused errors + return UNINITIALIZED; +#endif // SIMDJSON_SUPPORTS_CONCEPTS + } + + /** @overload template error_code get(T &out) & noexcept */ + template simdjson_deprecated simdjson_inline error_code get(T &out) && noexcept; + +#if SIMDJSON_EXCEPTIONS + /** + * Cast this JSON value to an instance of type T. The programmer is responsible for + * providing an implementation of get 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 + explicit simdjson_inline operator T() & noexcept(false); + template + 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 signed 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. + * + * @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) 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. + */ + 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. + */ + 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 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 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 at(size_t index) & noexcept; + /** + * Begin array iteration. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result begin() & noexcept; + /** + * Sentinel representing the end of the array. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result 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 find_field(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result 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 find_field_unordered(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result 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 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 is_scalar() noexcept; + + /** + * Checks whether the document is a string. + * + * @returns true if the type is string + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + */ + simdjson_inline simdjson_result is_string() noexcept; + + /** + * Checks whether the document is a negative number. + * + * @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 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 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 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 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 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 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 at_path(std::string_view json_path) noexcept; + + /** + * Get all values matching the given JSONPath expression with wildcard support. + * + * Supports wildcard patterns like "$.array[*]" or "$.object.*" to match multiple elements. + * + * This method materializes all matching values into a vector. + * The document will be consumed after this call. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern, or: + * - INVALID_JSON_POINTER if the JSONPath cannot be parsed + * - NO_SUCH_FIELD if a field does not exist + * - INDEX_OUT_OF_BOUNDS if an array index is out of bounds + * - INCORRECT_TYPE if path traversal encounters wrong type + */ + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + + /** + * Consumes the document and returns a string_view instance corresponding to the + * document as represented in JSON. It points inside the original byte array containing + * the JSON document. + */ + simdjson_inline simdjson_result 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 + requires(std::is_class_v && (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 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 get_array() & noexcept; + simdjson_inline simdjson_result get_object() & noexcept; + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result get_value() noexcept; + + simdjson_inline simdjson_result is_null() noexcept; + template + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + template + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::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(*this).get(); + } + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_CONCEPTS + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { + static_assert(!sizeof(T), "The get 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 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, double, or bool."); + static_cast(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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " 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(out); // to get rid of unused errors + return UNINITIALIZED; +#endif // SIMDJSON_SUPPORTS_CONCEPTS + } + + /** @overload template error_code get(T &out) & noexcept */ + template simdjson_inline error_code get(T &out) && noexcept; + simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (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 + explicit simdjson_inline operator T() noexcept(false); + simdjson_inline operator array() & noexcept(false); + simdjson_inline operator 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 raw_json_string() noexcept(false); + simdjson_inline operator bool() noexcept(false); + simdjson_inline operator value() noexcept(false); +#endif + simdjson_inline simdjson_result count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) & noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result end() & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(const char *key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + + simdjson_inline simdjson_result type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + + simdjson_inline simdjson_result current_location() noexcept; + simdjson_inline int32_t current_depth() const noexcept; + simdjson_inline bool is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + simdjson_inline simdjson_result raw_json_token() noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + +private: + document *doc{nullptr}; +}; +} // namespace ondemand +} // namespace fallback +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public fallback::implementation_simdjson_result_base { +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 get_array() & noexcept; + simdjson_inline simdjson_result get_object() & noexcept; + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result get_value() noexcept; + simdjson_inline simdjson_result is_null() noexcept; + + template simdjson_inline simdjson_result get() & noexcept; + template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept; + + template simdjson_inline error_code get(T &out) & noexcept; + template simdjson_inline error_code get(T &out) && noexcept; +#if SIMDJSON_EXCEPTIONS + + using fallback::implementation_simdjson_result_base::operator*; + using fallback::implementation_simdjson_result_base::operator->; + template ::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); + 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); + simdjson_inline operator fallback::ondemand::value() noexcept(false); +#endif + simdjson_inline simdjson_result count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) & noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result end() & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(const char *key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + simdjson_inline simdjson_result type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + simdjson_inline simdjson_result current_location() noexcept; + simdjson_inline int32_t current_depth() const noexcept; + simdjson_inline bool at_end() const noexcept; + simdjson_inline bool is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + /** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */ + simdjson_inline simdjson_result raw_json_token() noexcept; + + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (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 : public fallback::implementation_simdjson_result_base { +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 get_array() & noexcept; + simdjson_inline simdjson_result get_object() & noexcept; + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result get_value() noexcept; + simdjson_inline simdjson_result is_null() noexcept; + + template simdjson_inline simdjson_result get() & noexcept; + template simdjson_inline simdjson_result get() && noexcept; + + template simdjson_inline error_code get(T &out) & noexcept; + template simdjson_inline error_code get(T &out) && noexcept; +#if SIMDJSON_EXCEPTIONS + template + 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); + simdjson_inline operator fallback::ondemand::value() noexcept(false); +#endif + simdjson_inline simdjson_result count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) & noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result end() & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(const char *key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + simdjson_inline simdjson_result type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + simdjson_inline simdjson_result current_location() noexcept; + simdjson_inline simdjson_result current_depth() const noexcept; + simdjson_inline simdjson_result is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + /** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */ + simdjson_inline simdjson_result raw_json_token() noexcept; + + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (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 +#include +#include +#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(); + * + */ + inline size_t truncated_bytes() const noexcept; + + class iterator { + public: + using value_type = simdjson_result; + using reference = simdjson_result; + 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. + * + * 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) + */ + simdjson_inline document_stream( + ondemand::parser &parser, + const uint8_t *buf, + size_t len, + size_t batch_size, + bool allow_comma_separated + ) 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. */ + inline void next_document() 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; + /** + * 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 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. + */ + 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; + friend struct simdjson::internal::simdjson_result_base; +}; // document_stream + +} // namespace ondemand +} // namespace fallback +} // namespace simdjson + +namespace simdjson { +template<> +struct simdjson_result : public fallback::implementation_simdjson_result_base { +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 so you can use C++ algorithms that rely on pairs. + */ +class field : public std::pair { +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 unescaped_key(bool allow_replacement = false) 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. 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 + 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; + /** + * 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 start(value_iterator &parent_iter) noexcept; + static simdjson_inline simdjson_result start(const value_iterator &parent_iter, raw_json_string key) noexcept; + friend struct simdjson_result; + friend class object_iterator; +}; + +} // namespace ondemand +} // namespace fallback +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public fallback::implementation_simdjson_result_base { +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 unescaped_key(bool allow_replacement = false) noexcept; + template + simdjson_inline error_code unescaped_key(string_type &receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result key() noexcept; + simdjson_inline simdjson_result key_raw_json_token() noexcept; + simdjson_inline simdjson_result escaped_key() noexcept; + simdjson_inline simdjson_result value() noexcept; +}; + +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ONDEMAND_FIELD_H +/* end file simdjson/generic/ondemand/field.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 */ +/* 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 { + +/** + * 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 + */ + simdjson_inline simdjson_result begin() noexcept; + simdjson_inline simdjson_result 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"); + * ``` + * 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 find_field(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result 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 find_field_unordered(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) && 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("/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 at_pointer(std::string_view json_pointer) noexcept; + + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + inline simdjson_result at_path(std::string_view json_path) noexcept; + + /** + * Get all values matching the given JSONPath expression with wildcard support. + * Supports wildcard patterns like ".*" to match all object fields. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern + */ + inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + + /** + * Reset the iterator so that we are pointing back at the + * beginning of the object. You should still consume values only once even if you + * 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 reset() & 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 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 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 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + 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 + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(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 + requires(std::is_class_v && (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 start(value_iterator &iter) noexcept; + static simdjson_inline simdjson_result start_root(value_iterator &iter) noexcept; + static simdjson_inline simdjson_result 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{}; + + friend class value; + friend class document; + friend struct simdjson_result; +}; + +} // namespace ondemand +} // namespace fallback +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public fallback::implementation_simdjson_result_base { +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 begin() noexcept; + simdjson_inline simdjson_result end() noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) && noexcept; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + inline simdjson_result reset() noexcept; + inline simdjson_result is_empty() noexcept; + inline simdjson_result count_fields() & noexcept; + inline simdjson_result raw_json() noexcept; + #if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h + + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(out) ); + } + return SUCCESS; + } + +#if SIMDJSON_STATIC_REFLECTION + // TODO: move this code into object-inl.h + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) noexcept { + if (error()) { return error(); } + return first.extract_into(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; + + // + // Iterator interface + // + + // Reads key and value, yielding them to the user. + // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline simdjson_result 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}; +#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; + friend class object; +}; + +} // namespace ondemand +} // namespace fallback +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public fallback::implementation_simdjson_result_base { +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 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 &) const noexcept; + // Assumes it's being compared with the end. true if depth >= iter->depth. + simdjson_inline bool operator!=(const simdjson_result &) const noexcept; + // Checks for ']' and ',' + simdjson_inline simdjson_result &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/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 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 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 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 to_json_string(fallback::ondemand::array& x) noexcept; +inline simdjson_result to_json_string(simdjson_result x); +inline simdjson_result to_json_string(simdjson_result x); +inline simdjson_result to_json_string(simdjson_result x); +inline simdjson_result to_json_string(simdjson_result 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 + requires(!std::same_as && + !std::same_as && + !std::same_as && + !std::same_as) +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 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 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&& 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&& 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 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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +#include +#if SIMDJSON_STATIC_REFLECTION +#include +// #include // for std::define_static_string - header not available yet +#endif + +namespace simdjson { + +////////////////////////////// +// Number deserialization +////////////////////////////// + +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + uint64_t x; + SIMDJSON_TRY(val.get_uint64().get(x)); + if (x > (limits::max)()) { + return NUMBER_OUT_OF_RANGE; + } + out = static_cast(x); + return SUCCESS; +} + +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + double x; + SIMDJSON_TRY(val.get_double().get(x)); + out = static_cast(x); + return SUCCESS; +} + +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + 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(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 +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v) { + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + out = T{str}; + return SUCCESS; +} + + +/** + * 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>(). + */ +template +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { + using value_type = typename std::remove_cvref_t::value_type; + static_assert( + deserializable, + "The specified type inside the container must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the container must default constructible."); + fallback::ondemand::array arr; + if constexpr (std::is_same_v, fallback::ondemand::array>) { + arr = val; + } else { + SIMDJSON_TRY(val.get_array().get(arr)); + } + + for (auto v : arr) { + if constexpr (concepts::returns_reference) { + if (auto const err = v.get().get(concepts::emplace_one(out)); + err) { + // If an error occurs, the empty element that we just inserted gets + // removed. We're not using a temp variable because if T is a heavy + // type, we want the valid path to be the fast path and the slow path be + // the path that has errors in it. + if constexpr (requires { out.pop_back(); }) { + static_cast(out.pop_back()); + } + return err; + } + } else { + value_type temp; + if (auto const err = v.get().get(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 +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { + using value_type = typename std::remove_cvref_t::mapped_type; + static_assert( + deserializable, + "The specified value type inside the container must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "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().get(this_value)); + [[maybe_unused]] std::pair 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 +error_code tag_invoke(deserialize_tag, fallback::ondemand::object &obj, T &out) noexcept { + using value_type = typename std::remove_cvref_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 +error_code tag_invoke(deserialize_tag, fallback::ondemand::value &val, T &out) noexcept { + fallback::ondemand::object obj; + SIMDJSON_TRY(val.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} + +template +error_code tag_invoke(deserialize_tag, fallback::ondemand::document &doc, T &out) noexcept { + fallback::ondemand::object obj; + SIMDJSON_TRY(doc.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} + +template +error_code tag_invoke(deserialize_tag, fallback::ondemand::document_reference &doc, T &out) noexcept { + 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. + * + * If constructing T is nothrow, this conversion should be nothrow as well since + * we return MEMALLOC if we're not able to allocate memory instead of throwing + * the error message. + * + * @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 +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::element_type, ValT>) { + using element_type = typename std::remove_cvref_t::element_type; + + // For better error messages, don't use these as constraints on + // the tag_invoke CPO. + static_assert( + deserializable, + "The specified type inside the unique_ptr must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the unique_ptr must default constructible."); + + auto ptr = new (std::nothrow) element_type(); + if (ptr == nullptr) { + return MEMALLOC; + } + SIMDJSON_TRY(val.template get(*ptr)); + out.reset(ptr); + return SUCCESS; +} + +/** + * This CPO (Customization Point Object) will help deserialize into optional types. + */ +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable::value_type, decltype(val)>) { + using value_type = typename std::remove_cvref_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(out.value())); + return SUCCESS; +} + + +#if SIMDJSON_STATIC_REFLECTION + + +template +constexpr bool user_defined_type = (std::is_class_v +&& !std::is_same_v && !std::is_same_v && !concepts::optional_type && +!concepts::appendable_containers); + + +template + requires(user_defined_type && std::is_class_v) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { + fallback::ondemand::object obj; + if constexpr (std::is_same_v, fallback::ondemand::object>) { + obj = val; + } else { + SIMDJSON_TRY(val.get_object().get(obj)); + } + 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)); + if constexpr (concepts::optional_type) { + // for optional members, it's ok if the key is missing + auto error = obj[key].get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + if(error == NO_SUCH_FIELD) { + out.[:mem:].reset(); + continue; + } + return error; + } + } else { + // for non-optional members, the key must be present + SIMDJSON_TRY(obj[key].get(out.[:mem:])); + } + } + }; + return simdjson::SUCCESS; +} + +// Support for enum deserialization - deserialize from string representation using expand approach from P2996R12 +template + requires(std::is_enum_v) +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)); + constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T)); + template for (constexpr auto enum_val : enumerators) { + if (str == std::meta::identifier_of(enum_val)) { + out = [:enum_val:]; + return SUCCESS; + } + }; + + return INCORRECT_TYPE; +#else + // Fallback: deserialize as integer if reflection not available + std::underlying_type_t int_val; + SIMDJSON_TRY(val.get(int_val)); + out = static_cast(int_val); + return SUCCESS; +#endif +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr &out) noexcept { + if (!out) { + out = std::make_unique(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr &out) noexcept { + if (!out) { + out = std::make_shared(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +#endif // SIMDJSON_STATIC_REFLECTION + +//////////////////////////////////////// +// Unique pointers +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + 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 &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + 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 &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_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 &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_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 &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(); + } + int64_t temp; + SIMDJSON_TRY(val.get_int64().get(temp)); + *out = static_cast(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::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::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::started(value_iterator &iter) noexcept { + bool has_value; + SIMDJSON_TRY(iter.started_array().get(has_value)); + return array(iter); +} + +simdjson_inline simdjson_result array::begin() noexcept { +#if SIMDJSON_DEVELOPMENT_CHECKS + if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } +#endif + return array_iterator(iter); +} +simdjson_inline simdjson_result 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 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(starting_point), size_t(final_point - starting_point)); +} + +SIMDJSON_PUSH_DISABLE_WARNINGS +SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING +simdjson_inline simdjson_result array::count_elements() & 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 array after counting the number of elements. + iter.reset_array(); + return count; +} +SIMDJSON_POP_DISABLE_WARNINGS + +simdjson_inline simdjson_result array::is_empty() & noexcept { + bool is_not_empty; + auto error = iter.reset_array().get(is_not_empty); + if(error) { return error; } + return !is_not_empty; +} + +inline simdjson_result array::reset() & noexcept { + return iter.reset_array(); +} + +inline simdjson_result array::at_pointer(std::string_view json_pointer) noexcept { + if (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; } + + // Read the array index + size_t array_index = 0; + size_t i; + for (i = 0; i < json_pointer.length() && json_pointer[i] != '/'; i++) { + uint8_t digit = uint8_t(json_pointer[i] - '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; } + array_index = array_index*10 + digit; + } + + // 0 followed by other digits is invalid + if (i > 1 && json_pointer[0] == '0') { return INVALID_JSON_POINTER; } // "JSON pointer array index has other characters after 0" + + // Empty string is invalid; so is a "/" with no digits before it + if (i == 0) { return INVALID_JSON_POINTER; } // "Empty string in JSON pointer array index" + // 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 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); +} + +inline simdjson_result> array::at_path_with_wildcard(std::string_view json_path) noexcept { + std::vector result; + + 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; + // Wildcard case + if(key=="*"){ + for(auto element: *this){ + + if(element.error()){ + return element.error(); + } + + if(remaining_path.empty()){ + // Use value_unsafe() because we've already checked for errors above. + // The 'element' is a simdjson_result wrapper, and we need to extract + // the underlying value. value_unsafe() is safe here because error() returned false. + result.push_back(std::move(element).value_unsafe()); + + }else{ + auto nested_result = element.at_path_with_wildcard(remaining_path); + + if(nested_result.error()){ + return nested_result.error(); + } + // Same logic as above. + std::vector nested_matches = std::move(nested_result).value_unsafe(); + + result.insert(result.end(), + std::make_move_iterator(nested_matches.begin()), + std::make_move_iterator(nested_matches.end())); + } + } + return result; + }else{ + // Specific index case in which we access the element at the given index + size_t idx=0; + + for(char c:key){ + if(c < '0' || c > '9'){ + return INVALID_JSON_POINTER; + } + idx = idx*10 + (c - '0'); + } + + auto element = at(idx); + + if(element.error()){ + return element.error(); + } + + if(remaining_path.empty()){ + result.push_back(std::move(element).value_unsafe()); + return result; + }else{ + return element.at_path_with_wildcard(remaining_path); + } + } +} + +simdjson_inline simdjson_result array::at(size_t index) noexcept { + 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::simdjson_result( + fallback::ondemand::array &&value +) noexcept + : implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept + : implementation_simdjson_result_base(error) +{ +} + +simdjson_inline simdjson_result simdjson_result::begin() noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() noexcept { + if (error()) { return error(); } + return first.end(); +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::is_empty() & noexcept { + if (error()) { return error(); } + return first.is_empty(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path(json_path); +} +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path_with_wildcard(json_path); +} +simdjson_inline simdjson_result simdjson_result::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} +{} + +simdjson_inline simdjson_result 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::simdjson_result( + fallback::ondemand::array_iterator &&value +) noexcept + : fallback::implementation_simdjson_result_base(std::forward(value)) +{ + first.iter.assert_is_valid(); +} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : fallback::implementation_simdjson_result_base({}, error) +{ +} + +simdjson_inline simdjson_result simdjson_result::operator*() noexcept { + if (error()) { return error(); } + return *first; +} +simdjson_inline bool simdjson_result::operator==(const simdjson_result &other) const noexcept { + if (!first.iter.is_valid()) { return !error(); } + return first == other.first; +} +simdjson_inline bool simdjson_result::operator!=(const simdjson_result &other) const noexcept { + if (!first.iter.is_valid()) { return error(); } + return first != other.first; +} +simdjson_inline simdjson_result &simdjson_result::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::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 */ + +namespace simdjson { +namespace fallback { +namespace ondemand { + +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 value::get_array() noexcept { + return array::start(iter); +} +simdjson_inline simdjson_result value::get_object() noexcept { + return object::start(iter); +} +simdjson_inline simdjson_result value::start_or_resume_object() noexcept { + if (iter.at_start()) { + return get_object(); + } else { + return object::resume(iter); + } +} + +simdjson_inline simdjson_result value::get_raw_json_string() noexcept { + return iter.get_raw_json_string(); +} +simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { + return iter.get_string(allow_replacement); +} +template +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 value::get_wobbly_string() noexcept { + return iter.get_wobbly_string(); +} +simdjson_inline simdjson_result value::get_double() noexcept { + return iter.get_double(); +} +simdjson_inline simdjson_result value::get_double_in_string() noexcept { + return iter.get_double_in_string(); +} +simdjson_inline simdjson_result value::get_uint64() noexcept { + return iter.get_uint64(); +} +simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { + return iter.get_uint64_in_string(); +} +simdjson_inline simdjson_result value::get_int64() noexcept { + return iter.get_int64(); +} +simdjson_inline simdjson_result value::get_int64_in_string() noexcept { + return iter.get_int64_in_string(); +} +simdjson_inline simdjson_result value::get_bool() noexcept { + return iter.get_bool(); +} +simdjson_inline simdjson_result value::is_null() noexcept { + return iter.is_null(); +} + +template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result 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); } +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(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(bool& out) noexcept { return get_bool().get(out); } + +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline value::operator T() noexcept(false) { + return get(); +} +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 value::begin() & noexcept { + return get_array().begin(); +} +simdjson_inline simdjson_result value::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result value::count_elements() & noexcept { + simdjson_result 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 value::count_fields() & noexcept { + simdjson_result answer; + auto a = get_object(); + answer = a.count_fields(); + iter.move_at_start(); + return answer; +} +simdjson_inline simdjson_result value::at(size_t index) noexcept { + auto a = get_array(); + return a.at(index); +} + +simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result value::find_field(const char *key) noexcept { + return start_or_resume_object().find_field(key); +} + +simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result value::find_field_unordered(const char *key) noexcept { + return start_or_resume_object().find_field_unordered(key); +} + +simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { + return start_or_resume_object()[key]; +} +simdjson_inline simdjson_result value::operator[](const char *key) noexcept { + return start_or_resume_object()[key]; +} + +simdjson_inline simdjson_result value::type() noexcept { + return iter.type(); +} + +simdjson_inline simdjson_result 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 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 value::is_integer() noexcept { + return iter.is_integer(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { + return iter.get_number_type(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { + return iter.get_number(); +} + +simdjson_inline std::string_view value::raw_json_token() noexcept { + return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); +} + +simdjson_inline simdjson_result 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 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::at_pointer(std::string_view json_pointer) noexcept { + 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::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; + } +} + +inline simdjson_result> value::at_path_with_wildcard(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_with_wildcard(json_path); + case json_type::object: + return (*this).get_object().at_path_with_wildcard(json_path); + default: + return INVALID_JSON_POINTER; + } +} + +} // namespace ondemand +} // namespace fallback +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + fallback::ondemand::value &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base(error) +{ +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + if (error()) { return error(); } + return {}; +} + +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) noexcept { + if (error()) { return error(); } + return first.find_field(key); +} + +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} + +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) noexcept { + if (error()) { return error(); } + return first[key]; +} + +simdjson_inline simdjson_result simdjson_result::get_array() noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} + +template<> simdjson_inline error_code simdjson_result::get(fallback::ondemand::value &out) noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} + +template simdjson_inline simdjson_result simdjson_result::get() noexcept { + if (error()) { return error(); } + return first.get(); +} +template simdjson_inline error_code simdjson_result::get(T &out) noexcept { + if (error()) { return error(); } + return first.get(out); +} + +template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { + if (error()) { return error(); } + return std::move(first); +} + +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline simdjson_result::operator T() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first.get(); +} +simdjson_inline simdjson_result::operator fallback::ondemand::array() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator fallback::ondemand::object() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator fallback::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { + if (error()) { return error(); } + return first.raw_json(); +} + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { + if (error()) { return error(); } + return first.current_depth(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer( + std::string_view json_pointer) noexcept { + if (error()) { + return error(); + } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + +inline simdjson_result> simdjson_result::at_path_with_wildcard( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path_with_wildcard(json_path); +} + +} // 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 */ + +namespace simdjson { +namespace fallback { +namespace ondemand { + +simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept + : iter{std::forward(_iter)} +{ + logger::log_start_value(iter, "document"); +} + +simdjson_inline document document::start(json_iterator &&iter) noexcept { + return document(std::forward(iter)); +} + +inline void document::rewind() noexcept { + iter.rewind(); +} + +inline std::string document::to_debug_string() noexcept { + return iter.to_string(); +} + +inline simdjson_result 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 document::start_or_resume_object() noexcept { + if (iter.at_root()) { + return get_object(); + } else { + return object::resume(resume_value_iterator()); + } +} +simdjson_inline simdjson_result document::get_value() noexcept { + // Make sure we start any arrays or objects before returning, so that start_root_() + // 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 document::get_array() & noexcept { + auto value = get_root_value_iterator(); + return array::start_root(value); +} +simdjson_inline simdjson_result 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 document::get_uint64() noexcept { + return get_root_value_iterator().get_root_uint64(true); +} +simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { + return get_root_value_iterator().get_root_uint64_in_string(true); +} +simdjson_inline simdjson_result document::get_int64() noexcept { + return get_root_value_iterator().get_root_int64(true); +} +simdjson_inline simdjson_result document::get_int64_in_string() noexcept { + return get_root_value_iterator().get_root_int64_in_string(true); +} +simdjson_inline simdjson_result document::get_double() noexcept { + return get_root_value_iterator().get_root_double(true); +} +simdjson_inline simdjson_result document::get_double_in_string() noexcept { + return get_root_value_iterator().get_root_double_in_string(true); +} +simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { + return get_root_value_iterator().get_root_string(true, allow_replacement); +} +template +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 document::get_wobbly_string() noexcept { + return get_root_value_iterator().get_root_wobbly_string(true); +} +simdjson_inline simdjson_result document::get_raw_json_string() noexcept { + return get_root_value_iterator().get_root_raw_json_string(true); +} +simdjson_inline simdjson_result document::get_bool() noexcept { + return get_root_value_iterator().get_root_bool(true); +} +simdjson_inline simdjson_result document::is_null() noexcept { + return get_root_value_iterator().is_root_null(true); +} + +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result 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); } +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(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(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 document::get() && noexcept { return get_raw_json_string(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_value(); } + +#if SIMDJSON_EXCEPTIONS +template +simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } +template +simdjson_inline document::operator T() & noexcept(false) { return get(); } +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 document::count_elements() & noexcept { + auto a = get_array(); + simdjson_result 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 document::count_fields() & noexcept { + auto a = get_object(); + simdjson_result 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 document::at(size_t index) & noexcept { + auto a = get_array(); + return a.at(index); +} +simdjson_inline simdjson_result document::begin() & noexcept { + return get_array().begin(); +} +simdjson_inline simdjson_result document::end() & noexcept { + return {}; +} + +simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field(const char *key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::find_field_unordered(const char *key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { + return start_or_resume_object()[key]; +} +simdjson_inline simdjson_result 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 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(starting_point), size_t(final_point - starting_point)); +} + +simdjson_inline simdjson_result document::type() noexcept { + return get_root_value_iterator().type(); +} + +simdjson_inline simdjson_result 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 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 document::is_integer() noexcept { + return get_root_value_iterator().is_root_integer(true); +} + +simdjson_inline simdjson_result document::get_number_type() noexcept { + return get_root_value_iterator().get_root_number_type(true); +} + +simdjson_inline simdjson_result document::get_number() noexcept { + return get_root_value_iterator().get_root_number(true); +} + + +simdjson_inline simdjson_result document::raw_json_token() noexcept { + auto _iter = get_root_value_iterator(); + return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); +} + +simdjson_inline simdjson_result 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 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; + } +} + +simdjson_inline simdjson_result> document::at_path_with_wildcard(std::string_view json_path) noexcept { + rewind(); // Rewind the document each time 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().at_path_with_wildcard(json_path); + case json_type::object: + return (*this).get_object().at_path_with_wildcard(json_path); + default: + return INVALID_JSON_POINTER; + } +} + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (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) { + // 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::simdjson_result( + fallback::ondemand::document &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base( + error + ) +{ +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { + if (error()) { return error(); } + return first.get_value(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} + +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} + +template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; +template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first); +} +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(fallback::ondemand::document &out) & noexcept = delete; +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(fallback::ondemand::document &out) && noexcept { + if (error()) { return error(); } + out = std::forward(first); + return SUCCESS; +} + +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} + +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} + +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} + +simdjson_inline bool simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} + +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} + +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} + +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} + + +#if SIMDJSON_EXCEPTIONS +template ::value == false>::type> +simdjson_inline simdjson_result::operator T() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator fallback::ondemand::array() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator fallback::ondemand::object() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator fallback::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator fallback::ondemand::value() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline bool simdjson_result::at_end() const noexcept { + if (error()) { return error(); } + return first.at_end(); +} + + +simdjson_inline int32_t simdjson_result::current_depth() const noexcept { + if (error()) { return error(); } + return first.current_depth(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path(json_path); +} + +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path_with_wildcard(json_path); +} + +#if SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(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 document_reference::get_array() & noexcept { return doc->get_array(); } +simdjson_inline simdjson_result 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 document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } +simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } +simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } +template +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 document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } +simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } +simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } +simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } +simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline document_reference::operator T() noexcept(false) { return get(); } +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 document_reference::count_elements() & noexcept { return doc->count_elements(); } +simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } +simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } +simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } +simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } +simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } +simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } +simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } +simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } +simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } +simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } +simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } +simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } +simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } +simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } +simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } +simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } +simdjson_inline simdjson_result> document_reference::at_path_with_wildcard(std::string_view json_path) noexcept { return doc->at_path_with_wildcard(json_path); } +simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} +simdjson_inline document_reference::operator document&() const noexcept { return *doc; } +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code document_reference::extract_into(T& out) & noexcept { + return doc->extract_into(out); +} +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION +} // namespace ondemand +} // namespace fallback +} // namespace simdjson + + + +namespace simdjson { +simdjson_inline simdjson_result::simdjson_result(fallback::ondemand::document_reference value, error_code error) + noexcept : implementation_simdjson_result_base(std::forward(value), error) {} + + +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { + if (error()) { return error(); } + return first.get_value(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} +template <> +simdjson_warn_unused simdjson_inline error_code simdjson_result::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::get(fallback::ondemand::document_reference &out) && noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline simdjson_result::operator T() noexcept(false) { + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + if (error()) { throw simdjson_error(error()); } + return first.get(); +} +simdjson_inline simdjson_result::operator fallback::ondemand::array() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator fallback::ondemand::object() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator fallback::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator fallback::ondemand::value() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path_with_wildcard(json_path); +} +#if SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(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 +#include + +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 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 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 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 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 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 +) 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}, + 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}, + allow_comma_separated{false}, + 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 { + 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 document_stream::iterator::operator*() noexcept { + return simdjson_result(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; } + doc_index = batch_start; + 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 + 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(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; + } + } +} + +inline void document_stream::next_document() noexcept { + // 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(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; + if (remaining <= batch_size) { + return p.implementation->stage1(&buf[_batch_start], remaining, stage1_mode::streaming_final); + } else { + return p.implementation->stage1(&buf[_batch_start], batch_size, stage1_mode::streaming_partial); + } +} + +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 { + auto depth = stream->doc.iter.depth(); + auto cur_struct_index = stream->doc.iter._root - stream->parser->implementation->structural_indexes.get(); + + // If at root, process the first token to determine if scalar value + if (stream->doc.iter.at_root()) { + 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 + // TODO: We could remove trailing whitespaces + // This returns a string spanning from start of value to the beginning of the next document (excluded) + { + auto next_index = 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(stream->buf) + current_index(); + while(svlen > 1 && (std::isspace(start[svlen-1]) || start[svlen-1] == '\0')) { + svlen--; + } + return std::string_view(start, svlen); + } + } + cur_struct_index++; + } + + while (cur_struct_index <= static_cast(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(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::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base(error) +{ +} +simdjson_inline simdjson_result::simdjson_result( + fallback::ondemand::document_stream &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(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() {} + +simdjson_inline field::field(raw_json_string key, ondemand::value &&value) noexcept + : std::pair(key, std::forward(value)) +{ +} + +simdjson_inline simdjson_result 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::start(const value_iterator &parent_iter, raw_json_string key) noexcept { + return field(key, parent_iter.child()); +} + +simdjson_inline simdjson_warn_unused simdjson_result 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 answer = first.unescape(second.iter.json_iter(), allow_replacement); + first.consume(); + return answer; +} + +template +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) ); + 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(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(first.buf), end_quote - first.buf); +} + +simdjson_inline value &field::value() & noexcept { + return second; +} + +simdjson_inline value field::value() && noexcept { + return std::forward(*this).second; +} + +} // namespace ondemand +} // namespace fallback +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + fallback::ondemand::field &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base(error) +{ +} + +simdjson_inline simdjson_result simdjson_result::key() noexcept { + if (error()) { return error(); } + return first.key(); +} + +simdjson_inline simdjson_result simdjson_result::key_raw_json_token() noexcept { + if (error()) { return error(); } + return first.key_raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::escaped_key() noexcept { + if (error()) { return error(); } + return first.escaped_key(); +} + +simdjson_inline simdjson_result simdjson_result::unescaped_key(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.unescaped_key(allow_replacement); +} + +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::unescaped_key(string_type &receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.unescaped_key(receiver, allow_replacement); +} + +simdjson_inline simdjson_result simdjson_result::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(other.token)), + parser{other.parser}, + _string_buf_loc{other._string_buf_loc}, + error{other.error}, + _depth{other._depth}, + _root{other._root}, + _streaming{other._streaming} +{ + 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; + 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} + +{ + 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 '"': + if(*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(TAPE_ERROR, "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; +} + +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(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 json_iterator::current_location() const noexcept { + if (!is_alive()) { // Unrecoverable error + if (!at_root()) { + return reinterpret_cast(token.peek(-1)); + } else { + return reinterpret_cast(token.peek()); + } + } + if (at_end()) { + return OUT_OF_BOUNDS; + } + return reinterpret_cast(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 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 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::simdjson_result(fallback::ondemand::json_iterator &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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; + default: SIMDJSON_UNREACHABLE(); + } + return out; +} + +#if SIMDJSON_EXCEPTIONS +inline std::ostream& operator<<(std::ostream& out, simdjson_result &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::simdjson_result(fallback::ondemand::json_type &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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 +#include + +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 +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_s); + if (size <= 0) return std::string(); + std::unique_ptr 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 +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)...); +} + +template +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)...); + 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): #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 */ +/* amalgamation skipped (editor-only): #endif */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace fallback { +namespace ondemand { + +simdjson_inline simdjson_result 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} +simdjson_inline simdjson_result 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} +simdjson_inline simdjson_result object::operator[](const std::string_view key) & noexcept { + return find_field_unordered(key); +} +simdjson_inline simdjson_result object::operator[](const std::string_view key) && noexcept { + return std::forward(*this).find_field_unordered(key); +} +simdjson_inline simdjson_result object::find_field(const std::string_view key) & noexcept { + 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} +simdjson_inline simdjson_result object::find_field(const std::string_view key) && noexcept { + 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} + +simdjson_inline simdjson_result object::start(value_iterator &iter) noexcept { + SIMDJSON_TRY( iter.start_object().error() ); + return object(iter); +} +simdjson_inline simdjson_result 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 object::raw_json() noexcept { + 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(starting_point), size_t(final_point - starting_point)); +} + +simdjson_inline simdjson_result 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::begin() noexcept { +#if SIMDJSON_DEVELOPMENT_CHECKS + if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } +#endif + return object_iterator(iter); +} +simdjson_inline simdjson_result object::end() noexcept { + return object_iterator(iter); +} + +inline simdjson_result object::at_pointer(std::string_view json_pointer) noexcept { + if (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 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 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); +} + +inline simdjson_result> object::at_path_with_wildcard(std::string_view json_path) noexcept { + std::vector result; + + 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; + // 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()) { + result.push_back(std::move(val)); + } else { + auto nested_result = val.at_path_with_wildcard(remaining_path); + + if (nested_result.error()) { + return nested_result.error(); + } + // Extract and append all nested matches to our result + std::vector nested_vec; + SIMDJSON_TRY(std::move(nested_result).get(nested_vec)); + + result.insert(result.end(), + std::make_move_iterator(nested_vec.begin()), + std::make_move_iterator(nested_vec.end())); + } + } + return result; + } else { + value val; + SIMDJSON_TRY(find_field(key).get(val)); + + if (remaining_path.empty()) { + result.push_back(std::move(val)); + return result; + } else { + return val.at_path_with_wildcard(remaining_path); + } + } +} + +simdjson_inline simdjson_result 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 object::is_empty() & noexcept { + 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 object::reset() & noexcept { + return iter.reset_object(); +} + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (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) { + // 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::simdjson_result(fallback::ondemand::object &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(error) {} + +simdjson_inline simdjson_result simdjson_result::begin() noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() noexcept { + if (error()) { return error(); } + return first.end(); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) && noexcept { + if (error()) { return error(); } + return std::forward(first).find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) && noexcept { + if (error()) { return error(); } + return std::forward(first)[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) && noexcept { + if (error()) { return error(); } + return std::forward(first).find_field(key); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path_with_wildcard(json_path); +} + +inline simdjson_result simdjson_result::reset() noexcept { + if (error()) { return error(); } + return first.reset(); +} + +inline simdjson_result simdjson_result::is_empty() noexcept { + if (error()) { return error(); } + return first.is_empty(); +} + +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} + +simdjson_inline simdjson_result simdjson_result::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} +{} + +simdjson_inline simdjson_result 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::simdjson_result( + fallback::ondemand::object_iterator &&value +) noexcept + : implementation_simdjson_result_base(std::forward(value)) +{ + first.iter.assert_is_valid(); +} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base({}, error) +{ +} + +simdjson_inline simdjson_result simdjson_result::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::operator==(const simdjson_result &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::operator!=(const simdjson_result &other) const noexcept { + if (!first.iter.is_valid()) { return error(); } + return first != other.first; +} +// Checks for ']' and ',' +simdjson_inline simdjson_result &simdjson_result::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/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; + 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 parser::iterate(padded_string_view json) & noexcept { + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } + + json.remove_utf8_bom(); + + // 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(json.data()), json.length(), stage1_mode::regular) ); + return document::start({ reinterpret_cast(json.data()), this }); +} + +#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept { + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } + + json.remove_utf8_bom(); + + // 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(json.data()), json.length(), stage1_mode::regular); + if (err) { + if (err != UNCLOSED_STRING) + return err; + } + return document::start({ reinterpret_cast(json.data()), this, true }); +} +#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON + +simdjson_warn_unused simdjson_inline simdjson_result 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 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 parser::iterate(std::string_view json, size_t allocated) & noexcept { + return iterate(padded_string_view(json, allocated)); +} + +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(std::string &json) & noexcept { + return iterate(pad_with_reserve(json)); +} + +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const std::string &json) & noexcept { + return iterate(padded_string_view(json)); +} + +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const simdjson_result &result) & noexcept { + // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 parser::iterate(const simdjson_result &result) & noexcept { + // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 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(json.data()), json.length(), stage1_mode::regular) ); + return json_iterator(reinterpret_cast(json.data()), this); +} + +inline simdjson_result 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; + } + if(allow_comma_separated && batch_size < len) { batch_size = len; } + return document_stream(*this, buf, len, batch_size, allow_comma_separated); +} + +inline simdjson_result 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(buf), len, batch_size, allow_comma_separated); +} +inline simdjson_result 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 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 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 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_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 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(dst), end-dst); + dst = end; + return result; +} + +simdjson_inline simdjson_warn_unused simdjson_result 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(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 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& parser::get_parser_instance() { + std::unique_ptr& 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& 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 parser_instance = nullptr; + return parser_instance; +} + + +} // namespace ondemand +} // namespace fallback +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result(fallback::ondemand::parser &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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(buf); +} + +simdjson_inline char raw_json_string::operator[](size_t i) const noexcept { + return reinterpret_cast(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()); + } + const char * r{raw()}; + size_t pos{0}; + for(;pos < target.size();pos++) { + if(r[pos] != target[pos]) { return false; } + } + if(r[pos] != '"') { return false; } + return true; +} + +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 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 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::simdjson_result(fallback::ondemand::raw_json_string &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(error) {} + +simdjson_inline simdjson_result simdjson_result::raw() const noexcept { + if (error()) { return error(); } + return first.raw(); +} +simdjson_inline char simdjson_result::operator[](size_t i) const noexcept { + if (error()) { return error(); } + return first[i]; +} +simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::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 simdjson_result::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::simdjson_result(fallback::ondemand::token_iterator &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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 { + +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 value_iterator::start_object() noexcept { + SIMDJSON_TRY( start_container('{', "Not an object", "object") ); + return started_object(); +} + +simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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 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 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_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 value_iterator::start_array() noexcept { + SIMDJSON_TRY( start_container('[', "Not an array", "array") ); + return started_array(); +} + +simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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(!not_true); +} +simdjson_warn_unused simdjson_inline simdjson_result 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 value_iterator::get_string(bool allow_replacement) noexcept { + return get_raw_json_string().unescape(json_iter(), allow_replacement); +} +template +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; } + 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 value_iterator::get_wobbly_string() noexcept { + return get_raw_json_string().unescape_wobbly(json_iter()); +} +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_raw_json_string() noexcept { + auto json = peek_scalar("string"); + if (*json != '"') { return incorrect_type_error("Not a string"); } + advance_scalar("string"); + return raw_json_string(json+1); +} +simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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 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 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 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 value_iterator::is_integer() noexcept { + return numberparsing::is_integer(peek_non_root_scalar("integer")); +} +simdjson_inline simdjson_result value_iterator::get_number_type() noexcept { + return numberparsing::get_number_type(peek_non_root_scalar("integer")); +} +simdjson_inline simdjson_result 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 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 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.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 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.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 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 +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; } + 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 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 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; } + advance_scalar("string"); + return raw_json_string(json+1); +} +simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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.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 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.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 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 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 simdjson_result value_iterator::reset_array() noexcept { + if(error()) { return error(); } + move_at_container_start(); + return started_array(); +} + +simdjson_inline simdjson_result 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 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::simdjson_result(fallback::ondemand::value_iterator &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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 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 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 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 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 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 to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result 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 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 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&& x) { + if (x.error()) { throw simdjson::simdjson_error(x.error()); } + return (out << x.value()); +} +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& 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 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 emails; }; + * + * std::string email; + * path_accessor::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 +#include +#include + +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 +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 +consteval auto make_field_step(const char (&name)[N]) { + return path_step(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 +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 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 +struct path_accessor { + using value = ::simdjson::fallback::ondemand::value; + + static constexpr auto parser = json_path_parser(); + 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 + static inline simdjson_result access(DocOrValue& doc_or_val) noexcept { + // Validate path at compile time if T is a struct + if constexpr (std::is_class_v) { + 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(doc_or_val.get_value()); + } + + // Extract value at path directly into target with compile-time type validation + // Example: std::string name; path_accessor::extract_field(doc, name); + template + static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept { + static_assert(std::is_class_v, "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(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 + static consteval std::enable_if_t, 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 + static inline error_code extract_with_reflection(simdjson_result 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(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(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_value, target_ref); + } + } + } + // Skip unexpected characters and continue + else { + return extract_with_reflection(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 + static inline simdjson_result access_impl(simdjson_result 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_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_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_value); + } + } else { + return access_impl(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(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 + 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 + 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 + static consteval bool is_container_type() { + using BaseType = std::remove_cvref_t; + if constexpr (requires { typename BaseType::value_type; }) { + return true; + } + if constexpr (std::is_array_v) { + return true; + } + return false; + } + + // Extract element type from non-reflected container + template + using extract_element_type = std::conditional_t< + requires { typename std::remove_cvref_t::value_type; }, + typename std::remove_cvref_t::value_type, + std::conditional_t< + std::is_array_v>, + std::remove_extent_t>, + void + > + >; + + // Validate path matches struct definition + static consteval bool validate_path() { + if constexpr (!std::is_class_v) { + 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 +inline simdjson_result<::simdjson::fallback::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = path_accessor; + return accessor::access(doc_or_val); +} + +// Overload without type parameter (no validation) +template +inline simdjson_result<::simdjson::fallback::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = path_accessor; + return accessor::access(doc_or_val); +} + +// ============================================================================ +// JSON Pointer Compile-Time Support (RFC 6901) +// ============================================================================ + +// JSON Pointer parser: /field/0/nested (slash-separated) +template +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 +struct pointer_accessor { + using parser = json_pointer_parser; + 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) { + 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::is_array_like_reflected(current_type)) { + return false; + } + current_type = path_accessor::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 + static inline simdjson_result access_impl(simdjson_result 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(next_value); + } else { + auto obj = current.get_object().value_unsafe(); + auto next_value = obj.find_field_unordered(token); + return access_impl(next_value); + } + } + } + + // Access JSON value at pointer + template + static inline simdjson_result access(DocOrValue& doc_or_val) noexcept { + if constexpr (std::is_class_v) { + 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 current = doc_or_val.get_value(); + return access_impl<0>(current); + } + + // Extract value at pointer directly into target with type validation + template + static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept { + static_assert(std::is_class_v, "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 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 + static consteval std::enable_if_t, 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::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 +inline simdjson_result<::simdjson::fallback::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = pointer_accessor; + return accessor::access(doc_or_val); +} + +// Overload without type parameter (no validation) +template +inline simdjson_result<::simdjson::fallback::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = pointer_accessor; + 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 struct simd8; +template 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 +#include // visual studio or clang +#else +#include // 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 + * (or ) before, so the headers + * are fooled. + */ +#include // for _blsr_u64 +#include // for __lzcnt64 +#include // for most things (AVX2, AVX512, _popcnt64) +#include +#include +#include +#include +#include // 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 + +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { +#if SIMDJSON_REGULAR_VISUAL_STUDIO + return _addcarry_u64(0, value1, value2, + reinterpret_cast(result)); +#else + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); +#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(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 + 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(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; + + // Forward-declared so they can be used by splat and friends. + template + struct simd8; + + template> + struct base8: base> { + typedef uint32_t bitmask_t; + typedef uint64_t bitmask2_t; + + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m256i _value) : base>(_value) {} + + friend simdjson_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm256_cmpeq_epi8(lhs, rhs); } + + static const int SIZE = sizeof(base::value); + + template + simdjson_inline simd8 prev(const simd8 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: base8 { + static simdjson_inline simd8 splat(bool _value) { return _mm256_set1_epi8(uint8_t(-(!!_value))); } + + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m256i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(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 operator~() const { return *this ^ true; } + }; + + template + struct base8_numeric: base8 { + static simdjson_inline simd8 splat(T _value) { return _mm256_set1_epi8(_value); } + static simdjson_inline simd8 zero() { return _mm256_setzero_si256(); } + static simdjson_inline simd8 load(const T values[32]) { + return _mm256_loadu_si256(reinterpret_cast(values)); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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( + 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() {} + simdjson_inline base8_numeric(const __m256i _value) : base8(_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 operator+(const simd8 other) const { return _mm256_add_epi8(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return _mm256_sub_epi8(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } + + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } + + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 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 compress(uint32_t mask) would be + // sensible, but the AVX ISA makes this kind of approach difficult. + template + 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(pshufb_combine_table + pop1 * 8))); + __m256i compactmask = _mm256_insertf128_si256(v256, + _mm_loadu_si128(reinterpret_cast(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 + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; + + // Signed bytes + template<> + struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( + 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 max_val(const simd8 other) const { return _mm256_max_epi8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm256_min_epi8(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return _mm256_cmpgt_epi8(*this, other); } + simdjson_inline simd8 operator<(const simd8 other) const { return _mm256_cmpgt_epi8(other, *this); } + }; + + // Unsigned bytes + template<> + struct simd8: base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( + 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 saturating_add(const simd8 other) const { return _mm256_adds_epu8(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm256_subs_epu8(*this, other); } + + // Order-specific operations + simdjson_inline simd8 max_val(const simd8 other) const { return _mm256_max_epu8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm256_min_epu8(other, *this); } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } + simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } + simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } + simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } + + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } + simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } + simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } + simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return _mm256_testz_si256(*this, bits); } + simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } + template + simdjson_inline simd8 shr() const { return simd8(_mm256_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } + template + simdjson_inline simd8 shl() const { return simd8(_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 + simdjson_inline int get_bit() const { return _mm256_movemask_epi8(_mm256_slli_epi16(*this, 7-N)); } + }; + + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 2, "Haswell kernel should use two registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } + + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed + + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1) : chunks{chunk0, chunk1} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::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)*0); + this->chunks[1].store(ptr+sizeof(simd8)*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 reduce_or() const { + return this->chunks[0] | this->chunks[1]; + } + + simdjson_inline simd8x64 bit_or(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] | mask, + this->chunks[1] | mask + ); + } + + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] == mask, + this->chunks[1] == mask + ).to_bitmask(); + } + + simdjson_inline uint64_t eq(const simd8x64 &other) const { + return simd8x64( + 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 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] <= mask, + this->chunks[1] <= mask + ).to_bitmask(); + } + }; // struct simd8x64 + +} // 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 v(src); + // store to dest unconditionally - we can overwrite the bits we don't like later + v.store(dst); + return { + static_cast((v == '\\').to_bitmask()), // bs_bits + static_cast((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 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 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_BUILDER_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; + +} // 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 struct is_builtin_deserializable : std::false_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; + +template +concept is_builtin_deserializable_v = is_builtin_deserializable::value; + +template +concept custom_deserializable = tag_invocable; + +template +concept deserializable = custom_deserializable || is_builtin_deserializable_v || concepts::optional_type; + +template +concept nothrow_custom_deserializable = nothrow_tag_invocable; + +// built-in types are noexcept and if an error happens, the value simply gets ignored and the error is returned. +template +concept nothrow_deserializable = nothrow_custom_deserializable || is_builtin_deserializable_v; + +/// 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 + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(array_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for object + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(object_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for value + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document reference + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + 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 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 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 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 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 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 has_next_field() noexcept; + + /** + * Get the current field's key. + */ + simdjson_warn_unused simdjson_inline simdjson_result field_key() 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 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 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 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 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 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 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 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 get_string(bool allow_replacement) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_int64() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_double() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_bool() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result is_null() noexcept; + simdjson_warn_unused simdjson_inline bool is_negative() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result is_integer() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_number() noexcept; + + simdjson_warn_unused simdjson_inline simdjson_result get_root_string(bool check_trailing, bool allow_replacement) noexcept; + template + 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 get_root_wobbly_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_raw_json_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64_in_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_int64(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_int64_in_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_double(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_double_in_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_bool(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline bool is_root_negative() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result is_root_integer(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_number_type(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_number(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result 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 reset_array() noexcept; + /** + * Restarts an object iteration. + * @returns Whether the object has any fields (returns false for empty). + */ + simdjson_inline simdjson_result 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 parse_null(const uint8_t *json) const noexcept; + simdjson_inline simdjson_result 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 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; + /** @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 : public haswell::implementation_simdjson_result_base { +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 */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include + +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, uint64_t, int64_t, double, bool + * + * You may use get_double(), get_bool(), get_uint64(), get_int64(), + * 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 + simdjson_inline simdjson_result get() +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + #if SIMDJSON_SUPPORTS_CONCEPTS + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else if constexpr (concepts::optional_type) { + using value_type = typename std::remove_cvref_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(out.value()); + } else { + static_assert(!sizeof(T), "The get 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 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, double, or bool."); + static_cast(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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " 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(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 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 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 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 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 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 get_int64_in_string() 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 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 get_double_in_string() noexcept; + + /** + * Cast this JSON value to a string. + * + * The string is guaranteed to be valid UTF-8. + * + * Equivalent to get(). + * + * 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 get_string(bool allow_replacement = false) noexcept; + + /** + * 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 + 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 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 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 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 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 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 + 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 signed 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(). + * + * @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 begin() & noexcept; + /** + * Sentinel representing the end of the array. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result 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 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 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 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 find_field(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ + simdjson_inline simdjson_result 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 find_field_unordered(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result find_field_unordered(const char *key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result 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 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 is_scalar() noexcept; + /** + * Checks whether the value is a string. + * + * @returns true if the type is string + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + */ + simdjson_inline simdjson_result is_string() noexcept; + + /** + * Checks whether the value is a negative number. + * + * @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 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 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 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 raw_json() noexcept; + + /** + * Returns the current location in the document if in bounds. + */ + simdjson_inline simdjson_result 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 at_pointer(std::string_view json_pointer) noexcept; + + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + simdjson_inline simdjson_result at_path(std::string_view at_path) noexcept; + + /** + * Get all values matching the given JSONPath expression with wildcard support. + * Supports wildcard character (*) for arrays or ".*" for objects. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern + */ + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) 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 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; + friend struct simdjson_result; + friend class field; +}; + +} // namespace ondemand +} // namespace haswell +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public haswell::implementation_simdjson_result_base { +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 get_array() noexcept; + simdjson_inline simdjson_result get_object() noexcept; + + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result is_null() noexcept; + + template simdjson_inline simdjson_result get() noexcept; + + template simdjson_inline error_code get(T &out) noexcept; + +#if SIMDJSON_EXCEPTIONS + template + 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 count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result 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 find_field(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ + simdjson_inline simdjson_result 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 find_field_unordered(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result find_field_unordered(const char *key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result 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 type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + simdjson_inline simdjson_result is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + + /** @copydoc simdjson_inline std::string_view value::raw_json_token() const noexcept */ + simdjson_inline simdjson_result raw_json_token() noexcept; + simdjson_inline simdjson_result raw_json() noexcept; + + /** @copydoc simdjson_inline simdjson_result current_location() noexcept */ + simdjson_inline simdjson_result current_location() noexcept; + /** @copydoc simdjson_inline int32_t current_depth() const noexcept */ + simdjson_inline simdjson_result current_depth() const noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) 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 +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 +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 (`{` `}` `[` `]` `,` `:` `""` `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 value_iterator; + friend class object; + template + friend simdjson_inline void logger::log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta, logger::log_level level, Args&&... args) noexcept; + template + 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 : public haswell::implementation_simdjson_result_base { +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}; + +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; + + /** + * 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 unescape(raw_json_string in, bool allow_replacement) noexcept; + simdjson_inline simdjson_result 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 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 + 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 + 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 : public haswell::implementation_simdjson_result_base { +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 + 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 + 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 &type) noexcept(false); +#endif + +} // namespace ondemand +} // namespace haswell +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public haswell::implementation_simdjson_result_base { +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 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 unescape_wobbly(json_iterator &iter) const noexcept; + const uint8_t * buf{}; + friend class object; + friend class field; + friend class parser; + friend struct simdjson_result; +}; + +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 : public haswell::implementation_simdjson_result_base { +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 raw() const noexcept; + simdjson_inline char operator[](size_t) const noexcept; + simdjson_inline simdjson_warn_unused simdjson_result unescape(haswell::ondemand::json_iterator &iter, bool allow_replacement) const noexcept; + simdjson_inline simdjson_warn_unused simdjson_result 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 +#include + +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 iterate(padded_string_view json) & noexcept; +#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON + simdjson_warn_unused simdjson_result iterate_allow_incomplete_json(padded_string_view json) & noexcept; +#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const char *json, size_t len, size_t capacity) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const uint8_t *json, size_t len, size_t capacity) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(std::string_view json, size_t capacity) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const std::string &json) & noexcept; + /** @overload simdjson_result 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 iterate(std::string &json) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const simdjson_result &json) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const simdjson_result &json) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result 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 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 10MB, which has been a reasonable sweet spot in our tests. + * @param allow_comma_separated (defaults on false) This allows a mode where the documents are + * separated by commas instead of whitespace. It comes with a performance + * penalty because the entire document is indexed at once (and the document must be + * less than 4 GB), and there is no multithreading. In this mode, the batch_size parameter + * is effectively ignored, as it is set to at least the document size. + * @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 iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(padded_string_view json, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_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 iterate_many(std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @private We do not want to allow implicit conversion from C string to std::string. */ + simdjson_result iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete; + + /** 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 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 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: + friend bool release_parser(); + friend ondemand::parser& get_parser(); + /** Get the thread-local parser instance, allocates it if needed */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_parser_instance(); + /** Get the thread-local parser instance, it might be null */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_threadlocal_parser_if_exists(); + /** @private [for benchmarking access] The implementation to use */ + std::unique_ptr implementation{}; + size_t _capacity{0}; + size_t _max_capacity; + size_t _max_depth{DEFAULT_MAX_DEPTH}; + std::unique_ptr string_buf{}; + +#if SIMDJSON_DEVELOPMENT_CHECKS + std::unique_ptr start_positions{}; +#endif + + friend class json_iterator; + friend class document_stream; +}; + +} // namespace ondemand +} // namespace haswell +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public haswell::implementation_simdjson_result_base { +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 */ +/* 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. + */ + simdjson_inline simdjson_result begin() noexcept; + /** + * Sentinel representing the end of the array. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result 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 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 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 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 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 at_path(std::string_view json_path) noexcept; + + /** + * Get all values matching the given JSONPath expression with wildcard support. + * Supports wildcard patterns like "[*]" to match all array elements. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern + */ + inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + + /** + * Consumes the array and returns a string_view instance corresponding to the + * array as represented in JSON. It points inside the original document. + */ + simdjson_inline simdjson_result 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 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + 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 + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(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 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 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 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{}; + + friend class value; + friend class document; + friend struct simdjson_result; + friend struct simdjson_result; + friend class array_iterator; +}; + +} // namespace ondemand +} // namespace haswell +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public haswell::implementation_simdjson_result_base { +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 begin() noexcept; + simdjson_inline simdjson_result end() noexcept; + inline simdjson_result count_elements() & noexcept; + inline simdjson_result is_empty() & noexcept; + inline simdjson_result reset() & noexcept; + simdjson_inline simdjson_result at(size_t index) noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h + + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(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 */ +/* 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; + 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; + + // + // Iterator interface + // + + /** + * Get the current element. + * + * Part of the std::iterator interface. + */ + simdjson_inline simdjson_result + 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}; +#endif + value_iterator iter{}; + + simdjson_inline array_iterator(const value_iterator &iter) noexcept; + + friend class array; + friend class value; + friend struct simdjson_result; +}; + +} // namespace ondemand +} // namespace haswell +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public haswell::implementation_simdjson_result_base { + using iterator_category = std::input_iterator_tag; + using value_type = simdjson_result; + 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 operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline bool operator==(const simdjson_result &) const noexcept; + simdjson_inline bool operator!=(const simdjson_result &) const noexcept; + simdjson_inline simdjson_result &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 */ +/* 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 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 get_object() & noexcept; + /** + * Cast this JSON value to an unsigned integer. + * + * @returns A signed 64-bit integer. + * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. + */ + simdjson_inline simdjson_result get_uint64() noexcept; + /** + * Cast this JSON value (inside string) to an unsigned integer. + * + * @returns A signed 64-bit integer. + * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. + */ + simdjson_inline simdjson_result 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 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 get_int64_in_string() 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 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 get_double_in_string() noexcept; + /** + * 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 get_string(bool allow_replacement = false) noexcept; + /** + * 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 + 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 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 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 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 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 is_null() noexcept; + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool + * + * You may use get_double(), get_bool(), get_uint64(), get_int64(), + * 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 + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + /** + * @overload template simdjson_result 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 + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::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(*this).get(); + } + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_CONCEPTS + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { + static_assert(!sizeof(T), "The get 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 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, double, or bool."); + static_cast(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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " 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(out); // to get rid of unused errors + return UNINITIALIZED; +#endif // SIMDJSON_SUPPORTS_CONCEPTS + } + + /** @overload template error_code get(T &out) & noexcept */ + template simdjson_deprecated simdjson_inline error_code get(T &out) && noexcept; + +#if SIMDJSON_EXCEPTIONS + /** + * Cast this JSON value to an instance of type T. The programmer is responsible for + * providing an implementation of get 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 + explicit simdjson_inline operator T() & noexcept(false); + template + 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 signed 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. + * + * @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) 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. + */ + 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. + */ + 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 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 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 at(size_t index) & noexcept; + /** + * Begin array iteration. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result begin() & noexcept; + /** + * Sentinel representing the end of the array. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result 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 find_field(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result 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 find_field_unordered(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result 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 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 is_scalar() noexcept; + + /** + * Checks whether the document is a string. + * + * @returns true if the type is string + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + */ + simdjson_inline simdjson_result is_string() noexcept; + + /** + * Checks whether the document is a negative number. + * + * @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 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 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 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 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 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 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 at_path(std::string_view json_path) noexcept; + + /** + * Get all values matching the given JSONPath expression with wildcard support. + * + * Supports wildcard patterns like "$.array[*]" or "$.object.*" to match multiple elements. + * + * This method materializes all matching values into a vector. + * The document will be consumed after this call. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern, or: + * - INVALID_JSON_POINTER if the JSONPath cannot be parsed + * - NO_SUCH_FIELD if a field does not exist + * - INDEX_OUT_OF_BOUNDS if an array index is out of bounds + * - INCORRECT_TYPE if path traversal encounters wrong type + */ + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + + /** + * Consumes the document and returns a string_view instance corresponding to the + * document as represented in JSON. It points inside the original byte array containing + * the JSON document. + */ + simdjson_inline simdjson_result 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 + requires(std::is_class_v && (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 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 get_array() & noexcept; + simdjson_inline simdjson_result get_object() & noexcept; + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result get_value() noexcept; + + simdjson_inline simdjson_result is_null() noexcept; + template + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + template + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::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(*this).get(); + } + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_CONCEPTS + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { + static_assert(!sizeof(T), "The get 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 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, double, or bool."); + static_cast(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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " 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(out); // to get rid of unused errors + return UNINITIALIZED; +#endif // SIMDJSON_SUPPORTS_CONCEPTS + } + + /** @overload template error_code get(T &out) & noexcept */ + template simdjson_inline error_code get(T &out) && noexcept; + simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (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 + explicit simdjson_inline operator T() noexcept(false); + simdjson_inline operator array() & noexcept(false); + simdjson_inline operator 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 raw_json_string() noexcept(false); + simdjson_inline operator bool() noexcept(false); + simdjson_inline operator value() noexcept(false); +#endif + simdjson_inline simdjson_result count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) & noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result end() & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(const char *key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + + simdjson_inline simdjson_result type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + + simdjson_inline simdjson_result current_location() noexcept; + simdjson_inline int32_t current_depth() const noexcept; + simdjson_inline bool is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + simdjson_inline simdjson_result raw_json_token() noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + +private: + document *doc{nullptr}; +}; +} // namespace ondemand +} // namespace haswell +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public haswell::implementation_simdjson_result_base { +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 get_array() & noexcept; + simdjson_inline simdjson_result get_object() & noexcept; + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result get_value() noexcept; + simdjson_inline simdjson_result is_null() noexcept; + + template simdjson_inline simdjson_result get() & noexcept; + template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept; + + template simdjson_inline error_code get(T &out) & noexcept; + template simdjson_inline error_code get(T &out) && noexcept; +#if SIMDJSON_EXCEPTIONS + + using haswell::implementation_simdjson_result_base::operator*; + using haswell::implementation_simdjson_result_base::operator->; + template ::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); + 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); + simdjson_inline operator haswell::ondemand::value() noexcept(false); +#endif + simdjson_inline simdjson_result count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) & noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result end() & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(const char *key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + simdjson_inline simdjson_result type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + simdjson_inline simdjson_result current_location() noexcept; + simdjson_inline int32_t current_depth() const noexcept; + simdjson_inline bool at_end() const noexcept; + simdjson_inline bool is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + /** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */ + simdjson_inline simdjson_result raw_json_token() noexcept; + + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (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 : public haswell::implementation_simdjson_result_base { +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 get_array() & noexcept; + simdjson_inline simdjson_result get_object() & noexcept; + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result get_value() noexcept; + simdjson_inline simdjson_result is_null() noexcept; + + template simdjson_inline simdjson_result get() & noexcept; + template simdjson_inline simdjson_result get() && noexcept; + + template simdjson_inline error_code get(T &out) & noexcept; + template simdjson_inline error_code get(T &out) && noexcept; +#if SIMDJSON_EXCEPTIONS + template + 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); + simdjson_inline operator haswell::ondemand::value() noexcept(false); +#endif + simdjson_inline simdjson_result count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) & noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result end() & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(const char *key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + simdjson_inline simdjson_result type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + simdjson_inline simdjson_result current_location() noexcept; + simdjson_inline simdjson_result current_depth() const noexcept; + simdjson_inline simdjson_result is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + /** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */ + simdjson_inline simdjson_result raw_json_token() noexcept; + + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (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 +#include +#include +#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(); + * + */ + inline size_t truncated_bytes() const noexcept; + + class iterator { + public: + using value_type = simdjson_result; + using reference = simdjson_result; + 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. + * + * 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) + */ + simdjson_inline document_stream( + ondemand::parser &parser, + const uint8_t *buf, + size_t len, + size_t batch_size, + bool allow_comma_separated + ) 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. */ + inline void next_document() 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; + /** + * 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 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. + */ + 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; + friend struct simdjson::internal::simdjson_result_base; +}; // document_stream + +} // namespace ondemand +} // namespace haswell +} // namespace simdjson + +namespace simdjson { +template<> +struct simdjson_result : public haswell::implementation_simdjson_result_base { +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 so you can use C++ algorithms that rely on pairs. + */ +class field : public std::pair { +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 unescaped_key(bool allow_replacement = false) 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. 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 + 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; + /** + * 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 start(value_iterator &parent_iter) noexcept; + static simdjson_inline simdjson_result start(const value_iterator &parent_iter, raw_json_string key) noexcept; + friend struct simdjson_result; + friend class object_iterator; +}; + +} // namespace ondemand +} // namespace haswell +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public haswell::implementation_simdjson_result_base { +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 unescaped_key(bool allow_replacement = false) noexcept; + template + simdjson_inline error_code unescaped_key(string_type &receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result key() noexcept; + simdjson_inline simdjson_result key_raw_json_token() noexcept; + simdjson_inline simdjson_result escaped_key() noexcept; + simdjson_inline simdjson_result value() noexcept; +}; + +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ONDEMAND_FIELD_H +/* end file simdjson/generic/ondemand/field.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 */ +/* 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 { + +/** + * 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 + */ + simdjson_inline simdjson_result begin() noexcept; + simdjson_inline simdjson_result 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"); + * ``` + * 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 find_field(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result 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 find_field_unordered(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) && 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("/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 at_pointer(std::string_view json_pointer) noexcept; + + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + inline simdjson_result at_path(std::string_view json_path) noexcept; + + /** + * Get all values matching the given JSONPath expression with wildcard support. + * Supports wildcard patterns like ".*" to match all object fields. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern + */ + inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + + /** + * Reset the iterator so that we are pointing back at the + * beginning of the object. You should still consume values only once even if you + * 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 reset() & 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 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 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 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + 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 + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(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 + requires(std::is_class_v && (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 start(value_iterator &iter) noexcept; + static simdjson_inline simdjson_result start_root(value_iterator &iter) noexcept; + static simdjson_inline simdjson_result 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{}; + + friend class value; + friend class document; + friend struct simdjson_result; +}; + +} // namespace ondemand +} // namespace haswell +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public haswell::implementation_simdjson_result_base { +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 begin() noexcept; + simdjson_inline simdjson_result end() noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) && noexcept; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + inline simdjson_result reset() noexcept; + inline simdjson_result is_empty() noexcept; + inline simdjson_result count_fields() & noexcept; + inline simdjson_result raw_json() noexcept; + #if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h + + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(out) ); + } + return SUCCESS; + } + +#if SIMDJSON_STATIC_REFLECTION + // TODO: move this code into object-inl.h + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) noexcept { + if (error()) { return error(); } + return first.extract_into(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; + + // + // Iterator interface + // + + // Reads key and value, yielding them to the user. + // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline simdjson_result 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}; +#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; + friend class object; +}; + +} // namespace ondemand +} // namespace haswell +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public haswell::implementation_simdjson_result_base { +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 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 &) const noexcept; + // Assumes it's being compared with the end. true if depth >= iter->depth. + simdjson_inline bool operator!=(const simdjson_result &) const noexcept; + // Checks for ']' and ',' + simdjson_inline simdjson_result &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/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 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 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 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 to_json_string(haswell::ondemand::array& x) noexcept; +inline simdjson_result to_json_string(simdjson_result x); +inline simdjson_result to_json_string(simdjson_result x); +inline simdjson_result to_json_string(simdjson_result x); +inline simdjson_result to_json_string(simdjson_result 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 + requires(!std::same_as && + !std::same_as && + !std::same_as && + !std::same_as) +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 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 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&& 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&& 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 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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +#include +#if SIMDJSON_STATIC_REFLECTION +#include +// #include // for std::define_static_string - header not available yet +#endif + +namespace simdjson { + +////////////////////////////// +// Number deserialization +////////////////////////////// + +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + uint64_t x; + SIMDJSON_TRY(val.get_uint64().get(x)); + if (x > (limits::max)()) { + return NUMBER_OUT_OF_RANGE; + } + out = static_cast(x); + return SUCCESS; +} + +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + double x; + SIMDJSON_TRY(val.get_double().get(x)); + out = static_cast(x); + return SUCCESS; +} + +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + 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(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 +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v) { + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + out = T{str}; + return SUCCESS; +} + + +/** + * 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>(). + */ +template +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { + using value_type = typename std::remove_cvref_t::value_type; + static_assert( + deserializable, + "The specified type inside the container must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the container must default constructible."); + haswell::ondemand::array arr; + if constexpr (std::is_same_v, haswell::ondemand::array>) { + arr = val; + } else { + SIMDJSON_TRY(val.get_array().get(arr)); + } + + for (auto v : arr) { + if constexpr (concepts::returns_reference) { + if (auto const err = v.get().get(concepts::emplace_one(out)); + err) { + // If an error occurs, the empty element that we just inserted gets + // removed. We're not using a temp variable because if T is a heavy + // type, we want the valid path to be the fast path and the slow path be + // the path that has errors in it. + if constexpr (requires { out.pop_back(); }) { + static_cast(out.pop_back()); + } + return err; + } + } else { + value_type temp; + if (auto const err = v.get().get(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 +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { + using value_type = typename std::remove_cvref_t::mapped_type; + static_assert( + deserializable, + "The specified value type inside the container must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "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().get(this_value)); + [[maybe_unused]] std::pair 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 +error_code tag_invoke(deserialize_tag, haswell::ondemand::object &obj, T &out) noexcept { + using value_type = typename std::remove_cvref_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 +error_code tag_invoke(deserialize_tag, haswell::ondemand::value &val, T &out) noexcept { + haswell::ondemand::object obj; + SIMDJSON_TRY(val.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} + +template +error_code tag_invoke(deserialize_tag, haswell::ondemand::document &doc, T &out) noexcept { + haswell::ondemand::object obj; + SIMDJSON_TRY(doc.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} + +template +error_code tag_invoke(deserialize_tag, haswell::ondemand::document_reference &doc, T &out) noexcept { + 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. + * + * If constructing T is nothrow, this conversion should be nothrow as well since + * we return MEMALLOC if we're not able to allocate memory instead of throwing + * the error message. + * + * @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 +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::element_type, ValT>) { + using element_type = typename std::remove_cvref_t::element_type; + + // For better error messages, don't use these as constraints on + // the tag_invoke CPO. + static_assert( + deserializable, + "The specified type inside the unique_ptr must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the unique_ptr must default constructible."); + + auto ptr = new (std::nothrow) element_type(); + if (ptr == nullptr) { + return MEMALLOC; + } + SIMDJSON_TRY(val.template get(*ptr)); + out.reset(ptr); + return SUCCESS; +} + +/** + * This CPO (Customization Point Object) will help deserialize into optional types. + */ +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable::value_type, decltype(val)>) { + using value_type = typename std::remove_cvref_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(out.value())); + return SUCCESS; +} + + +#if SIMDJSON_STATIC_REFLECTION + + +template +constexpr bool user_defined_type = (std::is_class_v +&& !std::is_same_v && !std::is_same_v && !concepts::optional_type && +!concepts::appendable_containers); + + +template + requires(user_defined_type && std::is_class_v) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { + haswell::ondemand::object obj; + if constexpr (std::is_same_v, haswell::ondemand::object>) { + obj = val; + } else { + SIMDJSON_TRY(val.get_object().get(obj)); + } + 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)); + if constexpr (concepts::optional_type) { + // for optional members, it's ok if the key is missing + auto error = obj[key].get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + if(error == NO_SUCH_FIELD) { + out.[:mem:].reset(); + continue; + } + return error; + } + } else { + // for non-optional members, the key must be present + SIMDJSON_TRY(obj[key].get(out.[:mem:])); + } + } + }; + return simdjson::SUCCESS; +} + +// Support for enum deserialization - deserialize from string representation using expand approach from P2996R12 +template + requires(std::is_enum_v) +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)); + constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T)); + template for (constexpr auto enum_val : enumerators) { + if (str == std::meta::identifier_of(enum_val)) { + out = [:enum_val:]; + return SUCCESS; + } + }; + + return INCORRECT_TYPE; +#else + // Fallback: deserialize as integer if reflection not available + std::underlying_type_t int_val; + SIMDJSON_TRY(val.get(int_val)); + out = static_cast(int_val); + return SUCCESS; +#endif +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr &out) noexcept { + if (!out) { + out = std::make_unique(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr &out) noexcept { + if (!out) { + out = std::make_shared(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +#endif // SIMDJSON_STATIC_REFLECTION + +//////////////////////////////////////// +// Unique pointers +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + 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 &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + 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 &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_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 &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_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 &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(); + } + int64_t temp; + SIMDJSON_TRY(val.get_int64().get(temp)); + *out = static_cast(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::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::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::started(value_iterator &iter) noexcept { + bool has_value; + SIMDJSON_TRY(iter.started_array().get(has_value)); + return array(iter); +} + +simdjson_inline simdjson_result array::begin() noexcept { +#if SIMDJSON_DEVELOPMENT_CHECKS + if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } +#endif + return array_iterator(iter); +} +simdjson_inline simdjson_result 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 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(starting_point), size_t(final_point - starting_point)); +} + +SIMDJSON_PUSH_DISABLE_WARNINGS +SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING +simdjson_inline simdjson_result array::count_elements() & 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 array after counting the number of elements. + iter.reset_array(); + return count; +} +SIMDJSON_POP_DISABLE_WARNINGS + +simdjson_inline simdjson_result array::is_empty() & noexcept { + bool is_not_empty; + auto error = iter.reset_array().get(is_not_empty); + if(error) { return error; } + return !is_not_empty; +} + +inline simdjson_result array::reset() & noexcept { + return iter.reset_array(); +} + +inline simdjson_result array::at_pointer(std::string_view json_pointer) noexcept { + if (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; } + + // Read the array index + size_t array_index = 0; + size_t i; + for (i = 0; i < json_pointer.length() && json_pointer[i] != '/'; i++) { + uint8_t digit = uint8_t(json_pointer[i] - '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; } + array_index = array_index*10 + digit; + } + + // 0 followed by other digits is invalid + if (i > 1 && json_pointer[0] == '0') { return INVALID_JSON_POINTER; } // "JSON pointer array index has other characters after 0" + + // Empty string is invalid; so is a "/" with no digits before it + if (i == 0) { return INVALID_JSON_POINTER; } // "Empty string in JSON pointer array index" + // 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 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); +} + +inline simdjson_result> array::at_path_with_wildcard(std::string_view json_path) noexcept { + std::vector result; + + 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; + // Wildcard case + if(key=="*"){ + for(auto element: *this){ + + if(element.error()){ + return element.error(); + } + + if(remaining_path.empty()){ + // Use value_unsafe() because we've already checked for errors above. + // The 'element' is a simdjson_result wrapper, and we need to extract + // the underlying value. value_unsafe() is safe here because error() returned false. + result.push_back(std::move(element).value_unsafe()); + + }else{ + auto nested_result = element.at_path_with_wildcard(remaining_path); + + if(nested_result.error()){ + return nested_result.error(); + } + // Same logic as above. + std::vector nested_matches = std::move(nested_result).value_unsafe(); + + result.insert(result.end(), + std::make_move_iterator(nested_matches.begin()), + std::make_move_iterator(nested_matches.end())); + } + } + return result; + }else{ + // Specific index case in which we access the element at the given index + size_t idx=0; + + for(char c:key){ + if(c < '0' || c > '9'){ + return INVALID_JSON_POINTER; + } + idx = idx*10 + (c - '0'); + } + + auto element = at(idx); + + if(element.error()){ + return element.error(); + } + + if(remaining_path.empty()){ + result.push_back(std::move(element).value_unsafe()); + return result; + }else{ + return element.at_path_with_wildcard(remaining_path); + } + } +} + +simdjson_inline simdjson_result array::at(size_t index) noexcept { + 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::simdjson_result( + haswell::ondemand::array &&value +) noexcept + : implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept + : implementation_simdjson_result_base(error) +{ +} + +simdjson_inline simdjson_result simdjson_result::begin() noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() noexcept { + if (error()) { return error(); } + return first.end(); +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::is_empty() & noexcept { + if (error()) { return error(); } + return first.is_empty(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path(json_path); +} +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path_with_wildcard(json_path); +} +simdjson_inline simdjson_result simdjson_result::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} +{} + +simdjson_inline simdjson_result 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::simdjson_result( + haswell::ondemand::array_iterator &&value +) noexcept + : haswell::implementation_simdjson_result_base(std::forward(value)) +{ + first.iter.assert_is_valid(); +} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : haswell::implementation_simdjson_result_base({}, error) +{ +} + +simdjson_inline simdjson_result simdjson_result::operator*() noexcept { + if (error()) { return error(); } + return *first; +} +simdjson_inline bool simdjson_result::operator==(const simdjson_result &other) const noexcept { + if (!first.iter.is_valid()) { return !error(); } + return first == other.first; +} +simdjson_inline bool simdjson_result::operator!=(const simdjson_result &other) const noexcept { + if (!first.iter.is_valid()) { return error(); } + return first != other.first; +} +simdjson_inline simdjson_result &simdjson_result::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::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 */ + +namespace simdjson { +namespace haswell { +namespace ondemand { + +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 value::get_array() noexcept { + return array::start(iter); +} +simdjson_inline simdjson_result value::get_object() noexcept { + return object::start(iter); +} +simdjson_inline simdjson_result value::start_or_resume_object() noexcept { + if (iter.at_start()) { + return get_object(); + } else { + return object::resume(iter); + } +} + +simdjson_inline simdjson_result value::get_raw_json_string() noexcept { + return iter.get_raw_json_string(); +} +simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { + return iter.get_string(allow_replacement); +} +template +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 value::get_wobbly_string() noexcept { + return iter.get_wobbly_string(); +} +simdjson_inline simdjson_result value::get_double() noexcept { + return iter.get_double(); +} +simdjson_inline simdjson_result value::get_double_in_string() noexcept { + return iter.get_double_in_string(); +} +simdjson_inline simdjson_result value::get_uint64() noexcept { + return iter.get_uint64(); +} +simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { + return iter.get_uint64_in_string(); +} +simdjson_inline simdjson_result value::get_int64() noexcept { + return iter.get_int64(); +} +simdjson_inline simdjson_result value::get_int64_in_string() noexcept { + return iter.get_int64_in_string(); +} +simdjson_inline simdjson_result value::get_bool() noexcept { + return iter.get_bool(); +} +simdjson_inline simdjson_result value::is_null() noexcept { + return iter.is_null(); +} + +template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result 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); } +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(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(bool& out) noexcept { return get_bool().get(out); } + +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline value::operator T() noexcept(false) { + return get(); +} +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 value::begin() & noexcept { + return get_array().begin(); +} +simdjson_inline simdjson_result value::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result value::count_elements() & noexcept { + simdjson_result 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 value::count_fields() & noexcept { + simdjson_result answer; + auto a = get_object(); + answer = a.count_fields(); + iter.move_at_start(); + return answer; +} +simdjson_inline simdjson_result value::at(size_t index) noexcept { + auto a = get_array(); + return a.at(index); +} + +simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result value::find_field(const char *key) noexcept { + return start_or_resume_object().find_field(key); +} + +simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result value::find_field_unordered(const char *key) noexcept { + return start_or_resume_object().find_field_unordered(key); +} + +simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { + return start_or_resume_object()[key]; +} +simdjson_inline simdjson_result value::operator[](const char *key) noexcept { + return start_or_resume_object()[key]; +} + +simdjson_inline simdjson_result value::type() noexcept { + return iter.type(); +} + +simdjson_inline simdjson_result 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 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 value::is_integer() noexcept { + return iter.is_integer(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { + return iter.get_number_type(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { + return iter.get_number(); +} + +simdjson_inline std::string_view value::raw_json_token() noexcept { + return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); +} + +simdjson_inline simdjson_result 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 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::at_pointer(std::string_view json_pointer) noexcept { + 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::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; + } +} + +inline simdjson_result> value::at_path_with_wildcard(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_with_wildcard(json_path); + case json_type::object: + return (*this).get_object().at_path_with_wildcard(json_path); + default: + return INVALID_JSON_POINTER; + } +} + +} // namespace ondemand +} // namespace haswell +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + haswell::ondemand::value &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base(error) +{ +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + if (error()) { return error(); } + return {}; +} + +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) noexcept { + if (error()) { return error(); } + return first.find_field(key); +} + +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} + +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) noexcept { + if (error()) { return error(); } + return first[key]; +} + +simdjson_inline simdjson_result simdjson_result::get_array() noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} + +template<> simdjson_inline error_code simdjson_result::get(haswell::ondemand::value &out) noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} + +template simdjson_inline simdjson_result simdjson_result::get() noexcept { + if (error()) { return error(); } + return first.get(); +} +template simdjson_inline error_code simdjson_result::get(T &out) noexcept { + if (error()) { return error(); } + return first.get(out); +} + +template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { + if (error()) { return error(); } + return std::move(first); +} + +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline simdjson_result::operator T() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first.get(); +} +simdjson_inline simdjson_result::operator haswell::ondemand::array() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator haswell::ondemand::object() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator haswell::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { + if (error()) { return error(); } + return first.raw_json(); +} + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { + if (error()) { return error(); } + return first.current_depth(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer( + std::string_view json_pointer) noexcept { + if (error()) { + return error(); + } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + +inline simdjson_result> simdjson_result::at_path_with_wildcard( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path_with_wildcard(json_path); +} + +} // 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 */ + +namespace simdjson { +namespace haswell { +namespace ondemand { + +simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept + : iter{std::forward(_iter)} +{ + logger::log_start_value(iter, "document"); +} + +simdjson_inline document document::start(json_iterator &&iter) noexcept { + return document(std::forward(iter)); +} + +inline void document::rewind() noexcept { + iter.rewind(); +} + +inline std::string document::to_debug_string() noexcept { + return iter.to_string(); +} + +inline simdjson_result 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 document::start_or_resume_object() noexcept { + if (iter.at_root()) { + return get_object(); + } else { + return object::resume(resume_value_iterator()); + } +} +simdjson_inline simdjson_result document::get_value() noexcept { + // Make sure we start any arrays or objects before returning, so that start_root_() + // 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 document::get_array() & noexcept { + auto value = get_root_value_iterator(); + return array::start_root(value); +} +simdjson_inline simdjson_result 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 document::get_uint64() noexcept { + return get_root_value_iterator().get_root_uint64(true); +} +simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { + return get_root_value_iterator().get_root_uint64_in_string(true); +} +simdjson_inline simdjson_result document::get_int64() noexcept { + return get_root_value_iterator().get_root_int64(true); +} +simdjson_inline simdjson_result document::get_int64_in_string() noexcept { + return get_root_value_iterator().get_root_int64_in_string(true); +} +simdjson_inline simdjson_result document::get_double() noexcept { + return get_root_value_iterator().get_root_double(true); +} +simdjson_inline simdjson_result document::get_double_in_string() noexcept { + return get_root_value_iterator().get_root_double_in_string(true); +} +simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { + return get_root_value_iterator().get_root_string(true, allow_replacement); +} +template +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 document::get_wobbly_string() noexcept { + return get_root_value_iterator().get_root_wobbly_string(true); +} +simdjson_inline simdjson_result document::get_raw_json_string() noexcept { + return get_root_value_iterator().get_root_raw_json_string(true); +} +simdjson_inline simdjson_result document::get_bool() noexcept { + return get_root_value_iterator().get_root_bool(true); +} +simdjson_inline simdjson_result document::is_null() noexcept { + return get_root_value_iterator().is_root_null(true); +} + +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result 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); } +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(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(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 document::get() && noexcept { return get_raw_json_string(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_value(); } + +#if SIMDJSON_EXCEPTIONS +template +simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } +template +simdjson_inline document::operator T() & noexcept(false) { return get(); } +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 document::count_elements() & noexcept { + auto a = get_array(); + simdjson_result 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 document::count_fields() & noexcept { + auto a = get_object(); + simdjson_result 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 document::at(size_t index) & noexcept { + auto a = get_array(); + return a.at(index); +} +simdjson_inline simdjson_result document::begin() & noexcept { + return get_array().begin(); +} +simdjson_inline simdjson_result document::end() & noexcept { + return {}; +} + +simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field(const char *key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::find_field_unordered(const char *key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { + return start_or_resume_object()[key]; +} +simdjson_inline simdjson_result 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 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(starting_point), size_t(final_point - starting_point)); +} + +simdjson_inline simdjson_result document::type() noexcept { + return get_root_value_iterator().type(); +} + +simdjson_inline simdjson_result 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 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 document::is_integer() noexcept { + return get_root_value_iterator().is_root_integer(true); +} + +simdjson_inline simdjson_result document::get_number_type() noexcept { + return get_root_value_iterator().get_root_number_type(true); +} + +simdjson_inline simdjson_result document::get_number() noexcept { + return get_root_value_iterator().get_root_number(true); +} + + +simdjson_inline simdjson_result document::raw_json_token() noexcept { + auto _iter = get_root_value_iterator(); + return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); +} + +simdjson_inline simdjson_result 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 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; + } +} + +simdjson_inline simdjson_result> document::at_path_with_wildcard(std::string_view json_path) noexcept { + rewind(); // Rewind the document each time 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().at_path_with_wildcard(json_path); + case json_type::object: + return (*this).get_object().at_path_with_wildcard(json_path); + default: + return INVALID_JSON_POINTER; + } +} + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (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) { + // 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::simdjson_result( + haswell::ondemand::document &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base( + error + ) +{ +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { + if (error()) { return error(); } + return first.get_value(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} + +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} + +template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; +template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first); +} +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(haswell::ondemand::document &out) & noexcept = delete; +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(haswell::ondemand::document &out) && noexcept { + if (error()) { return error(); } + out = std::forward(first); + return SUCCESS; +} + +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} + +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} + +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} + +simdjson_inline bool simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} + +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} + +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} + +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} + + +#if SIMDJSON_EXCEPTIONS +template ::value == false>::type> +simdjson_inline simdjson_result::operator T() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator haswell::ondemand::array() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator haswell::ondemand::object() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator haswell::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator haswell::ondemand::value() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline bool simdjson_result::at_end() const noexcept { + if (error()) { return error(); } + return first.at_end(); +} + + +simdjson_inline int32_t simdjson_result::current_depth() const noexcept { + if (error()) { return error(); } + return first.current_depth(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path(json_path); +} + +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path_with_wildcard(json_path); +} + +#if SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(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 document_reference::get_array() & noexcept { return doc->get_array(); } +simdjson_inline simdjson_result 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 document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } +simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } +simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } +template +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 document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } +simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } +simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } +simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } +simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline document_reference::operator T() noexcept(false) { return get(); } +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 document_reference::count_elements() & noexcept { return doc->count_elements(); } +simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } +simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } +simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } +simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } +simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } +simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } +simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } +simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } +simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } +simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } +simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } +simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } +simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } +simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } +simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } +simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } +simdjson_inline simdjson_result> document_reference::at_path_with_wildcard(std::string_view json_path) noexcept { return doc->at_path_with_wildcard(json_path); } +simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} +simdjson_inline document_reference::operator document&() const noexcept { return *doc; } +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code document_reference::extract_into(T& out) & noexcept { + return doc->extract_into(out); +} +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION +} // namespace ondemand +} // namespace haswell +} // namespace simdjson + + + +namespace simdjson { +simdjson_inline simdjson_result::simdjson_result(haswell::ondemand::document_reference value, error_code error) + noexcept : implementation_simdjson_result_base(std::forward(value), error) {} + + +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { + if (error()) { return error(); } + return first.get_value(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} +template <> +simdjson_warn_unused simdjson_inline error_code simdjson_result::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::get(haswell::ondemand::document_reference &out) && noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline simdjson_result::operator T() noexcept(false) { + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + if (error()) { throw simdjson_error(error()); } + return first.get(); +} +simdjson_inline simdjson_result::operator haswell::ondemand::array() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator haswell::ondemand::object() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator haswell::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator haswell::ondemand::value() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path_with_wildcard(json_path); +} +#if SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(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 +#include + +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 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 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 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 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 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 +) 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}, + 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}, + allow_comma_separated{false}, + 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 { + 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 document_stream::iterator::operator*() noexcept { + return simdjson_result(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; } + doc_index = batch_start; + 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 + 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(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; + } + } +} + +inline void document_stream::next_document() noexcept { + // 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(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; + if (remaining <= batch_size) { + return p.implementation->stage1(&buf[_batch_start], remaining, stage1_mode::streaming_final); + } else { + return p.implementation->stage1(&buf[_batch_start], batch_size, stage1_mode::streaming_partial); + } +} + +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 { + auto depth = stream->doc.iter.depth(); + auto cur_struct_index = stream->doc.iter._root - stream->parser->implementation->structural_indexes.get(); + + // If at root, process the first token to determine if scalar value + if (stream->doc.iter.at_root()) { + 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 + // TODO: We could remove trailing whitespaces + // This returns a string spanning from start of value to the beginning of the next document (excluded) + { + auto next_index = 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(stream->buf) + current_index(); + while(svlen > 1 && (std::isspace(start[svlen-1]) || start[svlen-1] == '\0')) { + svlen--; + } + return std::string_view(start, svlen); + } + } + cur_struct_index++; + } + + while (cur_struct_index <= static_cast(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(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::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base(error) +{ +} +simdjson_inline simdjson_result::simdjson_result( + haswell::ondemand::document_stream &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(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() {} + +simdjson_inline field::field(raw_json_string key, ondemand::value &&value) noexcept + : std::pair(key, std::forward(value)) +{ +} + +simdjson_inline simdjson_result 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::start(const value_iterator &parent_iter, raw_json_string key) noexcept { + return field(key, parent_iter.child()); +} + +simdjson_inline simdjson_warn_unused simdjson_result 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 answer = first.unescape(second.iter.json_iter(), allow_replacement); + first.consume(); + return answer; +} + +template +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) ); + 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(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(first.buf), end_quote - first.buf); +} + +simdjson_inline value &field::value() & noexcept { + return second; +} + +simdjson_inline value field::value() && noexcept { + return std::forward(*this).second; +} + +} // namespace ondemand +} // namespace haswell +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + haswell::ondemand::field &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base(error) +{ +} + +simdjson_inline simdjson_result simdjson_result::key() noexcept { + if (error()) { return error(); } + return first.key(); +} + +simdjson_inline simdjson_result simdjson_result::key_raw_json_token() noexcept { + if (error()) { return error(); } + return first.key_raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::escaped_key() noexcept { + if (error()) { return error(); } + return first.escaped_key(); +} + +simdjson_inline simdjson_result simdjson_result::unescaped_key(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.unescaped_key(allow_replacement); +} + +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::unescaped_key(string_type &receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.unescaped_key(receiver, allow_replacement); +} + +simdjson_inline simdjson_result simdjson_result::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(other.token)), + parser{other.parser}, + _string_buf_loc{other._string_buf_loc}, + error{other.error}, + _depth{other._depth}, + _root{other._root}, + _streaming{other._streaming} +{ + 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; + 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} + +{ + 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 '"': + if(*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(TAPE_ERROR, "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; +} + +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(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 json_iterator::current_location() const noexcept { + if (!is_alive()) { // Unrecoverable error + if (!at_root()) { + return reinterpret_cast(token.peek(-1)); + } else { + return reinterpret_cast(token.peek()); + } + } + if (at_end()) { + return OUT_OF_BOUNDS; + } + return reinterpret_cast(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 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 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::simdjson_result(haswell::ondemand::json_iterator &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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; + default: SIMDJSON_UNREACHABLE(); + } + return out; +} + +#if SIMDJSON_EXCEPTIONS +inline std::ostream& operator<<(std::ostream& out, simdjson_result &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::simdjson_result(haswell::ondemand::json_type &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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 +#include + +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 +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_s); + if (size <= 0) return std::string(); + std::unique_ptr 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 +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)...); +} + +template +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)...); + 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): #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 */ +/* amalgamation skipped (editor-only): #endif */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace haswell { +namespace ondemand { + +simdjson_inline simdjson_result 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} +simdjson_inline simdjson_result 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} +simdjson_inline simdjson_result object::operator[](const std::string_view key) & noexcept { + return find_field_unordered(key); +} +simdjson_inline simdjson_result object::operator[](const std::string_view key) && noexcept { + return std::forward(*this).find_field_unordered(key); +} +simdjson_inline simdjson_result object::find_field(const std::string_view key) & noexcept { + 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} +simdjson_inline simdjson_result object::find_field(const std::string_view key) && noexcept { + 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} + +simdjson_inline simdjson_result object::start(value_iterator &iter) noexcept { + SIMDJSON_TRY( iter.start_object().error() ); + return object(iter); +} +simdjson_inline simdjson_result 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 object::raw_json() noexcept { + 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(starting_point), size_t(final_point - starting_point)); +} + +simdjson_inline simdjson_result 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::begin() noexcept { +#if SIMDJSON_DEVELOPMENT_CHECKS + if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } +#endif + return object_iterator(iter); +} +simdjson_inline simdjson_result object::end() noexcept { + return object_iterator(iter); +} + +inline simdjson_result object::at_pointer(std::string_view json_pointer) noexcept { + if (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 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 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); +} + +inline simdjson_result> object::at_path_with_wildcard(std::string_view json_path) noexcept { + std::vector result; + + 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; + // 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()) { + result.push_back(std::move(val)); + } else { + auto nested_result = val.at_path_with_wildcard(remaining_path); + + if (nested_result.error()) { + return nested_result.error(); + } + // Extract and append all nested matches to our result + std::vector nested_vec; + SIMDJSON_TRY(std::move(nested_result).get(nested_vec)); + + result.insert(result.end(), + std::make_move_iterator(nested_vec.begin()), + std::make_move_iterator(nested_vec.end())); + } + } + return result; + } else { + value val; + SIMDJSON_TRY(find_field(key).get(val)); + + if (remaining_path.empty()) { + result.push_back(std::move(val)); + return result; + } else { + return val.at_path_with_wildcard(remaining_path); + } + } +} + +simdjson_inline simdjson_result 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 object::is_empty() & noexcept { + 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 object::reset() & noexcept { + return iter.reset_object(); +} + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (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) { + // 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::simdjson_result(haswell::ondemand::object &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(error) {} + +simdjson_inline simdjson_result simdjson_result::begin() noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() noexcept { + if (error()) { return error(); } + return first.end(); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) && noexcept { + if (error()) { return error(); } + return std::forward(first).find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) && noexcept { + if (error()) { return error(); } + return std::forward(first)[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) && noexcept { + if (error()) { return error(); } + return std::forward(first).find_field(key); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path_with_wildcard(json_path); +} + +inline simdjson_result simdjson_result::reset() noexcept { + if (error()) { return error(); } + return first.reset(); +} + +inline simdjson_result simdjson_result::is_empty() noexcept { + if (error()) { return error(); } + return first.is_empty(); +} + +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} + +simdjson_inline simdjson_result simdjson_result::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} +{} + +simdjson_inline simdjson_result 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::simdjson_result( + haswell::ondemand::object_iterator &&value +) noexcept + : implementation_simdjson_result_base(std::forward(value)) +{ + first.iter.assert_is_valid(); +} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base({}, error) +{ +} + +simdjson_inline simdjson_result simdjson_result::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::operator==(const simdjson_result &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::operator!=(const simdjson_result &other) const noexcept { + if (!first.iter.is_valid()) { return error(); } + return first != other.first; +} +// Checks for ']' and ',' +simdjson_inline simdjson_result &simdjson_result::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/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; + 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 parser::iterate(padded_string_view json) & noexcept { + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } + + json.remove_utf8_bom(); + + // 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(json.data()), json.length(), stage1_mode::regular) ); + return document::start({ reinterpret_cast(json.data()), this }); +} + +#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept { + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } + + json.remove_utf8_bom(); + + // 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(json.data()), json.length(), stage1_mode::regular); + if (err) { + if (err != UNCLOSED_STRING) + return err; + } + return document::start({ reinterpret_cast(json.data()), this, true }); +} +#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON + +simdjson_warn_unused simdjson_inline simdjson_result 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 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 parser::iterate(std::string_view json, size_t allocated) & noexcept { + return iterate(padded_string_view(json, allocated)); +} + +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(std::string &json) & noexcept { + return iterate(pad_with_reserve(json)); +} + +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const std::string &json) & noexcept { + return iterate(padded_string_view(json)); +} + +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const simdjson_result &result) & noexcept { + // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 parser::iterate(const simdjson_result &result) & noexcept { + // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 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(json.data()), json.length(), stage1_mode::regular) ); + return json_iterator(reinterpret_cast(json.data()), this); +} + +inline simdjson_result 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; + } + if(allow_comma_separated && batch_size < len) { batch_size = len; } + return document_stream(*this, buf, len, batch_size, allow_comma_separated); +} + +inline simdjson_result 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(buf), len, batch_size, allow_comma_separated); +} +inline simdjson_result 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 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 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 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_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 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(dst), end-dst); + dst = end; + return result; +} + +simdjson_inline simdjson_warn_unused simdjson_result 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(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 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& parser::get_parser_instance() { + std::unique_ptr& 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& 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 parser_instance = nullptr; + return parser_instance; +} + + +} // namespace ondemand +} // namespace haswell +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result(haswell::ondemand::parser &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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(buf); +} + +simdjson_inline char raw_json_string::operator[](size_t i) const noexcept { + return reinterpret_cast(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()); + } + const char * r{raw()}; + size_t pos{0}; + for(;pos < target.size();pos++) { + if(r[pos] != target[pos]) { return false; } + } + if(r[pos] != '"') { return false; } + return true; +} + +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 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 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::simdjson_result(haswell::ondemand::raw_json_string &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(error) {} + +simdjson_inline simdjson_result simdjson_result::raw() const noexcept { + if (error()) { return error(); } + return first.raw(); +} +simdjson_inline char simdjson_result::operator[](size_t i) const noexcept { + if (error()) { return error(); } + return first[i]; +} +simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::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 simdjson_result::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::simdjson_result(haswell::ondemand::token_iterator &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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 { + +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 value_iterator::start_object() noexcept { + SIMDJSON_TRY( start_container('{', "Not an object", "object") ); + return started_object(); +} + +simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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 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 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_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 value_iterator::start_array() noexcept { + SIMDJSON_TRY( start_container('[', "Not an array", "array") ); + return started_array(); +} + +simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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(!not_true); +} +simdjson_warn_unused simdjson_inline simdjson_result 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 value_iterator::get_string(bool allow_replacement) noexcept { + return get_raw_json_string().unescape(json_iter(), allow_replacement); +} +template +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; } + 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 value_iterator::get_wobbly_string() noexcept { + return get_raw_json_string().unescape_wobbly(json_iter()); +} +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_raw_json_string() noexcept { + auto json = peek_scalar("string"); + if (*json != '"') { return incorrect_type_error("Not a string"); } + advance_scalar("string"); + return raw_json_string(json+1); +} +simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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 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 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 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 value_iterator::is_integer() noexcept { + return numberparsing::is_integer(peek_non_root_scalar("integer")); +} +simdjson_inline simdjson_result value_iterator::get_number_type() noexcept { + return numberparsing::get_number_type(peek_non_root_scalar("integer")); +} +simdjson_inline simdjson_result 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 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 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.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 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.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 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 +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; } + 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 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 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; } + advance_scalar("string"); + return raw_json_string(json+1); +} +simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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.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 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.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 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 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 simdjson_result value_iterator::reset_array() noexcept { + if(error()) { return error(); } + move_at_container_start(); + return started_array(); +} + +simdjson_inline simdjson_result 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 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::simdjson_result(haswell::ondemand::value_iterator &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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 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 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 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 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 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 to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result 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 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 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&& x) { + if (x.error()) { throw simdjson::simdjson_error(x.error()); } + return (out << x.value()); +} +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& 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 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 emails; }; + * + * std::string email; + * path_accessor::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 +#include +#include + +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 +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 +consteval auto make_field_step(const char (&name)[N]) { + return path_step(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 +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 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 +struct path_accessor { + using value = ::simdjson::haswell::ondemand::value; + + static constexpr auto parser = json_path_parser(); + 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 + static inline simdjson_result access(DocOrValue& doc_or_val) noexcept { + // Validate path at compile time if T is a struct + if constexpr (std::is_class_v) { + 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(doc_or_val.get_value()); + } + + // Extract value at path directly into target with compile-time type validation + // Example: std::string name; path_accessor::extract_field(doc, name); + template + static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept { + static_assert(std::is_class_v, "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(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 + static consteval std::enable_if_t, 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 + static inline error_code extract_with_reflection(simdjson_result 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(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(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_value, target_ref); + } + } + } + // Skip unexpected characters and continue + else { + return extract_with_reflection(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 + static inline simdjson_result access_impl(simdjson_result 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_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_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_value); + } + } else { + return access_impl(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(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 + 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 + 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 + static consteval bool is_container_type() { + using BaseType = std::remove_cvref_t; + if constexpr (requires { typename BaseType::value_type; }) { + return true; + } + if constexpr (std::is_array_v) { + return true; + } + return false; + } + + // Extract element type from non-reflected container + template + using extract_element_type = std::conditional_t< + requires { typename std::remove_cvref_t::value_type; }, + typename std::remove_cvref_t::value_type, + std::conditional_t< + std::is_array_v>, + std::remove_extent_t>, + void + > + >; + + // Validate path matches struct definition + static consteval bool validate_path() { + if constexpr (!std::is_class_v) { + 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 +inline simdjson_result<::simdjson::haswell::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = path_accessor; + return accessor::access(doc_or_val); +} + +// Overload without type parameter (no validation) +template +inline simdjson_result<::simdjson::haswell::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = path_accessor; + return accessor::access(doc_or_val); +} + +// ============================================================================ +// JSON Pointer Compile-Time Support (RFC 6901) +// ============================================================================ + +// JSON Pointer parser: /field/0/nested (slash-separated) +template +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 +struct pointer_accessor { + using parser = json_pointer_parser; + 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) { + 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::is_array_like_reflected(current_type)) { + return false; + } + current_type = path_accessor::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 + static inline simdjson_result access_impl(simdjson_result 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(next_value); + } else { + auto obj = current.get_object().value_unsafe(); + auto next_value = obj.find_field_unordered(token); + return access_impl(next_value); + } + } + } + + // Access JSON value at pointer + template + static inline simdjson_result access(DocOrValue& doc_or_val) noexcept { + if constexpr (std::is_class_v) { + 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 current = doc_or_val.get_value(); + return access_impl<0>(current); + } + + // Extract value at pointer directly into target with type validation + template + static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept { + static_assert(std::is_class_v, "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 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 + static consteval std::enable_if_t, 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::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 +inline simdjson_result<::simdjson::haswell::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = pointer_accessor; + return accessor::access(doc_or_val); +} + +// Overload without type parameter (no validation) +template +inline simdjson_result<::simdjson::haswell::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = pointer_accessor; + 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 +#include // visual studio or clang +#else +#include // 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 + * (or ) before, so the headers + * are fooled. + */ +#include // for _blsr_u64 +#include // for __lzcnt64 +#include // for most things (AVX2, AVX512, _popcnt64) +#include +#include +#include +#include +#include // for _mm_clmulepi64_si128 +// Important: we need the AVX-512 headers: +#include +#include +#include +#include +#include +#include +#include +// 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 + +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { +#if SIMDJSON_REGULAR_VISUAL_STUDIO + return _addcarry_u64(0, value1, value2, + reinterpret_cast(result)); +#else + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); +#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 + 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(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; + + // Forward-declared so they can be used by splat and friends. + template + struct simd8; + + template> + struct base8: base> { + typedef uint32_t bitmask_t; + typedef uint64_t bitmask2_t; + + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m512i _value) : base>(_value) {} + + friend simdjson_really_inline uint64_t operator==(const simd8 lhs, const simd8 rhs) { + return _mm512_cmpeq_epi8_mask(lhs, rhs); + } + static const int SIZE = sizeof(base::value); + + template + simdjson_inline simd8 prev(const simd8 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: base8 { + static simdjson_inline simd8 splat(bool _value) { return _mm512_set1_epi8(uint8_t(-(!!_value))); } + + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m512i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(splat(_value)) {} + simdjson_inline bool any() const { return !!_mm512_test_epi8_mask (*this, *this); } + simdjson_inline simd8 operator~() const { return *this ^ true; } + }; + + template + struct base8_numeric: base8 { + static simdjson_inline simd8 splat(T _value) { return _mm512_set1_epi8(_value); } + static simdjson_inline simd8 zero() { return _mm512_setzero_si512(); } + static simdjson_inline simd8 load(const T values[64]) { + return _mm512_loadu_si512(reinterpret_cast(values)); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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( + 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() {} + simdjson_inline base8_numeric(const __m512i _value) : base8(_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 operator+(const simd8 other) const { return _mm512_add_epi8(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return _mm512_sub_epi8(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } + + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } + + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 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 compress(uint32_t mask) would be + // sensible, but the AVX ISA makes this kind of approach difficult. + template + 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 + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; + + // Signed bytes + template<> + struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m512i _value) : base8_numeric(_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 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( + 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 max_val(const simd8 other) const { return _mm512_max_epi8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm512_min_epi8(*this, other); } + + simdjson_inline simd8 operator>(const simd8 other) const { return _mm512_maskz_abs_epi8(_mm512_cmpgt_epi8_mask(*this, other),_mm512_set1_epi8(uint8_t(0x80))); } + simdjson_inline simd8 operator<(const simd8 other) const { return _mm512_maskz_abs_epi8(_mm512_cmpgt_epi8_mask(other, *this),_mm512_set1_epi8(uint8_t(0x80))); } + }; + + // Unsigned bytes + template<> + struct simd8: base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m512i _value) : base8_numeric(_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 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( + 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 saturating_add(const simd8 other) const { return _mm512_adds_epu8(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm512_subs_epu8(*this, other); } + + // Order-specific operations + simdjson_inline simd8 max_val(const simd8 other) const { return _mm512_max_epu8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm512_min_epu8(other, *this); } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } + simdjson_inline uint64_t operator<=(const simd8 other) const { return other.max_val(*this) == other; } + simdjson_inline uint64_t operator>=(const simd8 other) const { return other.min_val(*this) == other; } + simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } + + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { return _mm512_mask_blend_epi8(*this == uint8_t(0), _mm512_set1_epi8(0), _mm512_set1_epi8(-1)); } + simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } + simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } + simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return !_mm512_test_epi8_mask(*this, bits); } + simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } + template + simdjson_inline simd8 shr() const { return simd8(_mm512_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } + template + simdjson_inline simd8 shl() const { return simd8(_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 + simdjson_inline uint64_t get_bit() const { return _mm512_movepi8_mask(_mm512_slli_epi16(*this, 7-N)); } + }; + + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 1, "Icelake kernel should use one register per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } + + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed + + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1) : chunks{chunk0, chunk1} {} + simdjson_inline simd8x64(const simd8 chunk0) : chunks{chunk0} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::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)*0); + } + + simdjson_inline simd8 reduce_or() const { + return this->chunks[0]; + } + + simdjson_inline simd8x64 bit_or(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] | mask + ); + } + + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return this->chunks[0] == mask; + } + + simdjson_inline uint64_t eq(const simd8x64 &other) const { + return this->chunks[0] == other.chunks[0]; + } + + simdjson_inline uint64_t lteq(const T m) const { + const simd8 mask = simd8::splat(m); + return this->chunks[0] <= mask; + } + }; // struct simd8x64 + +} // 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 v(src); + // store to dest unconditionally - we can overwrite the bits we don't like later + v.store(dst); + return { + static_cast(v == '\\'), // bs_bits + static_cast(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 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(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_BUILDER_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; + +} // 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 struct is_builtin_deserializable : std::false_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; + +template +concept is_builtin_deserializable_v = is_builtin_deserializable::value; + +template +concept custom_deserializable = tag_invocable; + +template +concept deserializable = custom_deserializable || is_builtin_deserializable_v || concepts::optional_type; + +template +concept nothrow_custom_deserializable = nothrow_tag_invocable; + +// built-in types are noexcept and if an error happens, the value simply gets ignored and the error is returned. +template +concept nothrow_deserializable = nothrow_custom_deserializable || is_builtin_deserializable_v; + +/// 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 + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(array_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for object + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(object_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for value + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document reference + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + 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 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 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 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 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 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 has_next_field() noexcept; + + /** + * Get the current field's key. + */ + simdjson_warn_unused simdjson_inline simdjson_result field_key() 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 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 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 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 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 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 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 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 get_string(bool allow_replacement) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_int64() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_double() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_bool() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result is_null() noexcept; + simdjson_warn_unused simdjson_inline bool is_negative() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result is_integer() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_number() noexcept; + + simdjson_warn_unused simdjson_inline simdjson_result get_root_string(bool check_trailing, bool allow_replacement) noexcept; + template + 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 get_root_wobbly_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_raw_json_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64_in_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_int64(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_int64_in_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_double(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_double_in_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_bool(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline bool is_root_negative() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result is_root_integer(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_number_type(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_number(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result 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 reset_array() noexcept; + /** + * Restarts an object iteration. + * @returns Whether the object has any fields (returns false for empty). + */ + simdjson_inline simdjson_result 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 parse_null(const uint8_t *json) const noexcept; + simdjson_inline simdjson_result 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 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; + /** @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 : public icelake::implementation_simdjson_result_base { +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 */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include + +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, uint64_t, int64_t, double, bool + * + * You may use get_double(), get_bool(), get_uint64(), get_int64(), + * 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 + simdjson_inline simdjson_result get() +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + #if SIMDJSON_SUPPORTS_CONCEPTS + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else if constexpr (concepts::optional_type) { + using value_type = typename std::remove_cvref_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(out.value()); + } else { + static_assert(!sizeof(T), "The get 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 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, double, or bool."); + static_cast(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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " 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(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 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 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 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 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 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 get_int64_in_string() 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 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 get_double_in_string() noexcept; + + /** + * Cast this JSON value to a string. + * + * The string is guaranteed to be valid UTF-8. + * + * Equivalent to get(). + * + * 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 get_string(bool allow_replacement = false) noexcept; + + /** + * 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 + 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 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 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 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 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 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 + 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 signed 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(). + * + * @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 begin() & noexcept; + /** + * Sentinel representing the end of the array. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result 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 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 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 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 find_field(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ + simdjson_inline simdjson_result 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 find_field_unordered(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result find_field_unordered(const char *key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result 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 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 is_scalar() noexcept; + /** + * Checks whether the value is a string. + * + * @returns true if the type is string + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + */ + simdjson_inline simdjson_result is_string() noexcept; + + /** + * Checks whether the value is a negative number. + * + * @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 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 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 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 raw_json() noexcept; + + /** + * Returns the current location in the document if in bounds. + */ + simdjson_inline simdjson_result 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 at_pointer(std::string_view json_pointer) noexcept; + + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + simdjson_inline simdjson_result at_path(std::string_view at_path) noexcept; + + /** + * Get all values matching the given JSONPath expression with wildcard support. + * Supports wildcard character (*) for arrays or ".*" for objects. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern + */ + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) 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 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; + friend struct simdjson_result; + friend class field; +}; + +} // namespace ondemand +} // namespace icelake +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public icelake::implementation_simdjson_result_base { +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 get_array() noexcept; + simdjson_inline simdjson_result get_object() noexcept; + + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result is_null() noexcept; + + template simdjson_inline simdjson_result get() noexcept; + + template simdjson_inline error_code get(T &out) noexcept; + +#if SIMDJSON_EXCEPTIONS + template + 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 count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result 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 find_field(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ + simdjson_inline simdjson_result 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 find_field_unordered(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result find_field_unordered(const char *key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result 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 type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + simdjson_inline simdjson_result is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + + /** @copydoc simdjson_inline std::string_view value::raw_json_token() const noexcept */ + simdjson_inline simdjson_result raw_json_token() noexcept; + simdjson_inline simdjson_result raw_json() noexcept; + + /** @copydoc simdjson_inline simdjson_result current_location() noexcept */ + simdjson_inline simdjson_result current_location() noexcept; + /** @copydoc simdjson_inline int32_t current_depth() const noexcept */ + simdjson_inline simdjson_result current_depth() const noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) 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 +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 +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 (`{` `}` `[` `]` `,` `:` `""` `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 value_iterator; + friend class object; + template + friend simdjson_inline void logger::log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta, logger::log_level level, Args&&... args) noexcept; + template + 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 : public icelake::implementation_simdjson_result_base { +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}; + +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; + + /** + * 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 unescape(raw_json_string in, bool allow_replacement) noexcept; + simdjson_inline simdjson_result 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 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 + 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 + 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 : public icelake::implementation_simdjson_result_base { +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 + 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 + 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 &type) noexcept(false); +#endif + +} // namespace ondemand +} // namespace icelake +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public icelake::implementation_simdjson_result_base { +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 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 unescape_wobbly(json_iterator &iter) const noexcept; + const uint8_t * buf{}; + friend class object; + friend class field; + friend class parser; + friend struct simdjson_result; +}; + +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 : public icelake::implementation_simdjson_result_base { +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 raw() const noexcept; + simdjson_inline char operator[](size_t) const noexcept; + simdjson_inline simdjson_warn_unused simdjson_result unescape(icelake::ondemand::json_iterator &iter, bool allow_replacement) const noexcept; + simdjson_inline simdjson_warn_unused simdjson_result 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 +#include + +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 iterate(padded_string_view json) & noexcept; +#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON + simdjson_warn_unused simdjson_result iterate_allow_incomplete_json(padded_string_view json) & noexcept; +#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const char *json, size_t len, size_t capacity) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const uint8_t *json, size_t len, size_t capacity) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(std::string_view json, size_t capacity) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const std::string &json) & noexcept; + /** @overload simdjson_result 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 iterate(std::string &json) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const simdjson_result &json) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const simdjson_result &json) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result 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 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 10MB, which has been a reasonable sweet spot in our tests. + * @param allow_comma_separated (defaults on false) This allows a mode where the documents are + * separated by commas instead of whitespace. It comes with a performance + * penalty because the entire document is indexed at once (and the document must be + * less than 4 GB), and there is no multithreading. In this mode, the batch_size parameter + * is effectively ignored, as it is set to at least the document size. + * @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 iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(padded_string_view json, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_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 iterate_many(std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @private We do not want to allow implicit conversion from C string to std::string. */ + simdjson_result iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete; + + /** 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 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 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: + friend bool release_parser(); + friend ondemand::parser& get_parser(); + /** Get the thread-local parser instance, allocates it if needed */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_parser_instance(); + /** Get the thread-local parser instance, it might be null */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_threadlocal_parser_if_exists(); + /** @private [for benchmarking access] The implementation to use */ + std::unique_ptr implementation{}; + size_t _capacity{0}; + size_t _max_capacity; + size_t _max_depth{DEFAULT_MAX_DEPTH}; + std::unique_ptr string_buf{}; + +#if SIMDJSON_DEVELOPMENT_CHECKS + std::unique_ptr start_positions{}; +#endif + + friend class json_iterator; + friend class document_stream; +}; + +} // namespace ondemand +} // namespace icelake +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public icelake::implementation_simdjson_result_base { +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 */ +/* 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. + */ + simdjson_inline simdjson_result begin() noexcept; + /** + * Sentinel representing the end of the array. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result 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 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 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 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 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 at_path(std::string_view json_path) noexcept; + + /** + * Get all values matching the given JSONPath expression with wildcard support. + * Supports wildcard patterns like "[*]" to match all array elements. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern + */ + inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + + /** + * Consumes the array and returns a string_view instance corresponding to the + * array as represented in JSON. It points inside the original document. + */ + simdjson_inline simdjson_result 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 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + 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 + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(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 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 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 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{}; + + friend class value; + friend class document; + friend struct simdjson_result; + friend struct simdjson_result; + friend class array_iterator; +}; + +} // namespace ondemand +} // namespace icelake +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public icelake::implementation_simdjson_result_base { +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 begin() noexcept; + simdjson_inline simdjson_result end() noexcept; + inline simdjson_result count_elements() & noexcept; + inline simdjson_result is_empty() & noexcept; + inline simdjson_result reset() & noexcept; + simdjson_inline simdjson_result at(size_t index) noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h + + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(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 */ +/* 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; + 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; + + // + // Iterator interface + // + + /** + * Get the current element. + * + * Part of the std::iterator interface. + */ + simdjson_inline simdjson_result + 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}; +#endif + value_iterator iter{}; + + simdjson_inline array_iterator(const value_iterator &iter) noexcept; + + friend class array; + friend class value; + friend struct simdjson_result; +}; + +} // namespace ondemand +} // namespace icelake +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public icelake::implementation_simdjson_result_base { + using iterator_category = std::input_iterator_tag; + using value_type = simdjson_result; + 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 operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline bool operator==(const simdjson_result &) const noexcept; + simdjson_inline bool operator!=(const simdjson_result &) const noexcept; + simdjson_inline simdjson_result &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 */ +/* 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 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 get_object() & noexcept; + /** + * Cast this JSON value to an unsigned integer. + * + * @returns A signed 64-bit integer. + * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. + */ + simdjson_inline simdjson_result get_uint64() noexcept; + /** + * Cast this JSON value (inside string) to an unsigned integer. + * + * @returns A signed 64-bit integer. + * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. + */ + simdjson_inline simdjson_result 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 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 get_int64_in_string() 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 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 get_double_in_string() noexcept; + /** + * 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 get_string(bool allow_replacement = false) noexcept; + /** + * 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 + 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 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 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 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 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 is_null() noexcept; + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool + * + * You may use get_double(), get_bool(), get_uint64(), get_int64(), + * 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 + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + /** + * @overload template simdjson_result 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 + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::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(*this).get(); + } + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_CONCEPTS + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { + static_assert(!sizeof(T), "The get 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 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, double, or bool."); + static_cast(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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " 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(out); // to get rid of unused errors + return UNINITIALIZED; +#endif // SIMDJSON_SUPPORTS_CONCEPTS + } + + /** @overload template error_code get(T &out) & noexcept */ + template simdjson_deprecated simdjson_inline error_code get(T &out) && noexcept; + +#if SIMDJSON_EXCEPTIONS + /** + * Cast this JSON value to an instance of type T. The programmer is responsible for + * providing an implementation of get 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 + explicit simdjson_inline operator T() & noexcept(false); + template + 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 signed 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. + * + * @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) 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. + */ + 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. + */ + 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 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 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 at(size_t index) & noexcept; + /** + * Begin array iteration. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result begin() & noexcept; + /** + * Sentinel representing the end of the array. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result 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 find_field(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result 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 find_field_unordered(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result 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 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 is_scalar() noexcept; + + /** + * Checks whether the document is a string. + * + * @returns true if the type is string + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + */ + simdjson_inline simdjson_result is_string() noexcept; + + /** + * Checks whether the document is a negative number. + * + * @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 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 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 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 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 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 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 at_path(std::string_view json_path) noexcept; + + /** + * Get all values matching the given JSONPath expression with wildcard support. + * + * Supports wildcard patterns like "$.array[*]" or "$.object.*" to match multiple elements. + * + * This method materializes all matching values into a vector. + * The document will be consumed after this call. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern, or: + * - INVALID_JSON_POINTER if the JSONPath cannot be parsed + * - NO_SUCH_FIELD if a field does not exist + * - INDEX_OUT_OF_BOUNDS if an array index is out of bounds + * - INCORRECT_TYPE if path traversal encounters wrong type + */ + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + + /** + * Consumes the document and returns a string_view instance corresponding to the + * document as represented in JSON. It points inside the original byte array containing + * the JSON document. + */ + simdjson_inline simdjson_result 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 + requires(std::is_class_v && (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 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 get_array() & noexcept; + simdjson_inline simdjson_result get_object() & noexcept; + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result get_value() noexcept; + + simdjson_inline simdjson_result is_null() noexcept; + template + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + template + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::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(*this).get(); + } + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_CONCEPTS + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { + static_assert(!sizeof(T), "The get 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 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, double, or bool."); + static_cast(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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " 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(out); // to get rid of unused errors + return UNINITIALIZED; +#endif // SIMDJSON_SUPPORTS_CONCEPTS + } + + /** @overload template error_code get(T &out) & noexcept */ + template simdjson_inline error_code get(T &out) && noexcept; + simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (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 + explicit simdjson_inline operator T() noexcept(false); + simdjson_inline operator array() & noexcept(false); + simdjson_inline operator 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 raw_json_string() noexcept(false); + simdjson_inline operator bool() noexcept(false); + simdjson_inline operator value() noexcept(false); +#endif + simdjson_inline simdjson_result count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) & noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result end() & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(const char *key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + + simdjson_inline simdjson_result type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + + simdjson_inline simdjson_result current_location() noexcept; + simdjson_inline int32_t current_depth() const noexcept; + simdjson_inline bool is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + simdjson_inline simdjson_result raw_json_token() noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + +private: + document *doc{nullptr}; +}; +} // namespace ondemand +} // namespace icelake +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public icelake::implementation_simdjson_result_base { +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 get_array() & noexcept; + simdjson_inline simdjson_result get_object() & noexcept; + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result get_value() noexcept; + simdjson_inline simdjson_result is_null() noexcept; + + template simdjson_inline simdjson_result get() & noexcept; + template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept; + + template simdjson_inline error_code get(T &out) & noexcept; + template simdjson_inline error_code get(T &out) && noexcept; +#if SIMDJSON_EXCEPTIONS + + using icelake::implementation_simdjson_result_base::operator*; + using icelake::implementation_simdjson_result_base::operator->; + template ::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); + 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); + simdjson_inline operator icelake::ondemand::value() noexcept(false); +#endif + simdjson_inline simdjson_result count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) & noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result end() & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(const char *key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + simdjson_inline simdjson_result type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + simdjson_inline simdjson_result current_location() noexcept; + simdjson_inline int32_t current_depth() const noexcept; + simdjson_inline bool at_end() const noexcept; + simdjson_inline bool is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + /** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */ + simdjson_inline simdjson_result raw_json_token() noexcept; + + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (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 : public icelake::implementation_simdjson_result_base { +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 get_array() & noexcept; + simdjson_inline simdjson_result get_object() & noexcept; + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result get_value() noexcept; + simdjson_inline simdjson_result is_null() noexcept; + + template simdjson_inline simdjson_result get() & noexcept; + template simdjson_inline simdjson_result get() && noexcept; + + template simdjson_inline error_code get(T &out) & noexcept; + template simdjson_inline error_code get(T &out) && noexcept; +#if SIMDJSON_EXCEPTIONS + template + 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); + simdjson_inline operator icelake::ondemand::value() noexcept(false); +#endif + simdjson_inline simdjson_result count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) & noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result end() & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(const char *key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + simdjson_inline simdjson_result type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + simdjson_inline simdjson_result current_location() noexcept; + simdjson_inline simdjson_result current_depth() const noexcept; + simdjson_inline simdjson_result is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + /** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */ + simdjson_inline simdjson_result raw_json_token() noexcept; + + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (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 +#include +#include +#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(); + * + */ + inline size_t truncated_bytes() const noexcept; + + class iterator { + public: + using value_type = simdjson_result; + using reference = simdjson_result; + 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. + * + * 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) + */ + simdjson_inline document_stream( + ondemand::parser &parser, + const uint8_t *buf, + size_t len, + size_t batch_size, + bool allow_comma_separated + ) 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. */ + inline void next_document() 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; + /** + * 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 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. + */ + 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; + friend struct simdjson::internal::simdjson_result_base; +}; // document_stream + +} // namespace ondemand +} // namespace icelake +} // namespace simdjson + +namespace simdjson { +template<> +struct simdjson_result : public icelake::implementation_simdjson_result_base { +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 so you can use C++ algorithms that rely on pairs. + */ +class field : public std::pair { +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 unescaped_key(bool allow_replacement = false) 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. 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 + 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; + /** + * 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 start(value_iterator &parent_iter) noexcept; + static simdjson_inline simdjson_result start(const value_iterator &parent_iter, raw_json_string key) noexcept; + friend struct simdjson_result; + friend class object_iterator; +}; + +} // namespace ondemand +} // namespace icelake +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public icelake::implementation_simdjson_result_base { +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 unescaped_key(bool allow_replacement = false) noexcept; + template + simdjson_inline error_code unescaped_key(string_type &receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result key() noexcept; + simdjson_inline simdjson_result key_raw_json_token() noexcept; + simdjson_inline simdjson_result escaped_key() noexcept; + simdjson_inline simdjson_result value() noexcept; +}; + +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ONDEMAND_FIELD_H +/* end file simdjson/generic/ondemand/field.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 */ +/* 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 { + +/** + * 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 + */ + simdjson_inline simdjson_result begin() noexcept; + simdjson_inline simdjson_result 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"); + * ``` + * 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 find_field(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result 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 find_field_unordered(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) && 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("/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 at_pointer(std::string_view json_pointer) noexcept; + + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + inline simdjson_result at_path(std::string_view json_path) noexcept; + + /** + * Get all values matching the given JSONPath expression with wildcard support. + * Supports wildcard patterns like ".*" to match all object fields. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern + */ + inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + + /** + * Reset the iterator so that we are pointing back at the + * beginning of the object. You should still consume values only once even if you + * 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 reset() & 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 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 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 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + 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 + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(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 + requires(std::is_class_v && (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 start(value_iterator &iter) noexcept; + static simdjson_inline simdjson_result start_root(value_iterator &iter) noexcept; + static simdjson_inline simdjson_result 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{}; + + friend class value; + friend class document; + friend struct simdjson_result; +}; + +} // namespace ondemand +} // namespace icelake +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public icelake::implementation_simdjson_result_base { +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 begin() noexcept; + simdjson_inline simdjson_result end() noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) && noexcept; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + inline simdjson_result reset() noexcept; + inline simdjson_result is_empty() noexcept; + inline simdjson_result count_fields() & noexcept; + inline simdjson_result raw_json() noexcept; + #if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h + + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(out) ); + } + return SUCCESS; + } + +#if SIMDJSON_STATIC_REFLECTION + // TODO: move this code into object-inl.h + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) noexcept { + if (error()) { return error(); } + return first.extract_into(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; + + // + // Iterator interface + // + + // Reads key and value, yielding them to the user. + // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline simdjson_result 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}; +#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; + friend class object; +}; + +} // namespace ondemand +} // namespace icelake +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public icelake::implementation_simdjson_result_base { +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 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 &) const noexcept; + // Assumes it's being compared with the end. true if depth >= iter->depth. + simdjson_inline bool operator!=(const simdjson_result &) const noexcept; + // Checks for ']' and ',' + simdjson_inline simdjson_result &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/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 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 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 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 to_json_string(icelake::ondemand::array& x) noexcept; +inline simdjson_result to_json_string(simdjson_result x); +inline simdjson_result to_json_string(simdjson_result x); +inline simdjson_result to_json_string(simdjson_result x); +inline simdjson_result to_json_string(simdjson_result 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 + requires(!std::same_as && + !std::same_as && + !std::same_as && + !std::same_as) +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 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 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&& 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&& 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 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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +#include +#if SIMDJSON_STATIC_REFLECTION +#include +// #include // for std::define_static_string - header not available yet +#endif + +namespace simdjson { + +////////////////////////////// +// Number deserialization +////////////////////////////// + +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + uint64_t x; + SIMDJSON_TRY(val.get_uint64().get(x)); + if (x > (limits::max)()) { + return NUMBER_OUT_OF_RANGE; + } + out = static_cast(x); + return SUCCESS; +} + +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + double x; + SIMDJSON_TRY(val.get_double().get(x)); + out = static_cast(x); + return SUCCESS; +} + +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + 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(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 +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v) { + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + out = T{str}; + return SUCCESS; +} + + +/** + * 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>(). + */ +template +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { + using value_type = typename std::remove_cvref_t::value_type; + static_assert( + deserializable, + "The specified type inside the container must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the container must default constructible."); + icelake::ondemand::array arr; + if constexpr (std::is_same_v, icelake::ondemand::array>) { + arr = val; + } else { + SIMDJSON_TRY(val.get_array().get(arr)); + } + + for (auto v : arr) { + if constexpr (concepts::returns_reference) { + if (auto const err = v.get().get(concepts::emplace_one(out)); + err) { + // If an error occurs, the empty element that we just inserted gets + // removed. We're not using a temp variable because if T is a heavy + // type, we want the valid path to be the fast path and the slow path be + // the path that has errors in it. + if constexpr (requires { out.pop_back(); }) { + static_cast(out.pop_back()); + } + return err; + } + } else { + value_type temp; + if (auto const err = v.get().get(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 +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { + using value_type = typename std::remove_cvref_t::mapped_type; + static_assert( + deserializable, + "The specified value type inside the container must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "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().get(this_value)); + [[maybe_unused]] std::pair 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 +error_code tag_invoke(deserialize_tag, icelake::ondemand::object &obj, T &out) noexcept { + using value_type = typename std::remove_cvref_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 +error_code tag_invoke(deserialize_tag, icelake::ondemand::value &val, T &out) noexcept { + icelake::ondemand::object obj; + SIMDJSON_TRY(val.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} + +template +error_code tag_invoke(deserialize_tag, icelake::ondemand::document &doc, T &out) noexcept { + icelake::ondemand::object obj; + SIMDJSON_TRY(doc.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} + +template +error_code tag_invoke(deserialize_tag, icelake::ondemand::document_reference &doc, T &out) noexcept { + 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. + * + * If constructing T is nothrow, this conversion should be nothrow as well since + * we return MEMALLOC if we're not able to allocate memory instead of throwing + * the error message. + * + * @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 +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::element_type, ValT>) { + using element_type = typename std::remove_cvref_t::element_type; + + // For better error messages, don't use these as constraints on + // the tag_invoke CPO. + static_assert( + deserializable, + "The specified type inside the unique_ptr must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the unique_ptr must default constructible."); + + auto ptr = new (std::nothrow) element_type(); + if (ptr == nullptr) { + return MEMALLOC; + } + SIMDJSON_TRY(val.template get(*ptr)); + out.reset(ptr); + return SUCCESS; +} + +/** + * This CPO (Customization Point Object) will help deserialize into optional types. + */ +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable::value_type, decltype(val)>) { + using value_type = typename std::remove_cvref_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(out.value())); + return SUCCESS; +} + + +#if SIMDJSON_STATIC_REFLECTION + + +template +constexpr bool user_defined_type = (std::is_class_v +&& !std::is_same_v && !std::is_same_v && !concepts::optional_type && +!concepts::appendable_containers); + + +template + requires(user_defined_type && std::is_class_v) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { + icelake::ondemand::object obj; + if constexpr (std::is_same_v, icelake::ondemand::object>) { + obj = val; + } else { + SIMDJSON_TRY(val.get_object().get(obj)); + } + 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)); + if constexpr (concepts::optional_type) { + // for optional members, it's ok if the key is missing + auto error = obj[key].get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + if(error == NO_SUCH_FIELD) { + out.[:mem:].reset(); + continue; + } + return error; + } + } else { + // for non-optional members, the key must be present + SIMDJSON_TRY(obj[key].get(out.[:mem:])); + } + } + }; + return simdjson::SUCCESS; +} + +// Support for enum deserialization - deserialize from string representation using expand approach from P2996R12 +template + requires(std::is_enum_v) +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)); + constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T)); + template for (constexpr auto enum_val : enumerators) { + if (str == std::meta::identifier_of(enum_val)) { + out = [:enum_val:]; + return SUCCESS; + } + }; + + return INCORRECT_TYPE; +#else + // Fallback: deserialize as integer if reflection not available + std::underlying_type_t int_val; + SIMDJSON_TRY(val.get(int_val)); + out = static_cast(int_val); + return SUCCESS; +#endif +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr &out) noexcept { + if (!out) { + out = std::make_unique(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr &out) noexcept { + if (!out) { + out = std::make_shared(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +#endif // SIMDJSON_STATIC_REFLECTION + +//////////////////////////////////////// +// Unique pointers +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + 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 &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + 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 &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_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 &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_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 &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(); + } + int64_t temp; + SIMDJSON_TRY(val.get_int64().get(temp)); + *out = static_cast(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::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::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::started(value_iterator &iter) noexcept { + bool has_value; + SIMDJSON_TRY(iter.started_array().get(has_value)); + return array(iter); +} + +simdjson_inline simdjson_result array::begin() noexcept { +#if SIMDJSON_DEVELOPMENT_CHECKS + if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } +#endif + return array_iterator(iter); +} +simdjson_inline simdjson_result 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 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(starting_point), size_t(final_point - starting_point)); +} + +SIMDJSON_PUSH_DISABLE_WARNINGS +SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING +simdjson_inline simdjson_result array::count_elements() & 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 array after counting the number of elements. + iter.reset_array(); + return count; +} +SIMDJSON_POP_DISABLE_WARNINGS + +simdjson_inline simdjson_result array::is_empty() & noexcept { + bool is_not_empty; + auto error = iter.reset_array().get(is_not_empty); + if(error) { return error; } + return !is_not_empty; +} + +inline simdjson_result array::reset() & noexcept { + return iter.reset_array(); +} + +inline simdjson_result array::at_pointer(std::string_view json_pointer) noexcept { + if (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; } + + // Read the array index + size_t array_index = 0; + size_t i; + for (i = 0; i < json_pointer.length() && json_pointer[i] != '/'; i++) { + uint8_t digit = uint8_t(json_pointer[i] - '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; } + array_index = array_index*10 + digit; + } + + // 0 followed by other digits is invalid + if (i > 1 && json_pointer[0] == '0') { return INVALID_JSON_POINTER; } // "JSON pointer array index has other characters after 0" + + // Empty string is invalid; so is a "/" with no digits before it + if (i == 0) { return INVALID_JSON_POINTER; } // "Empty string in JSON pointer array index" + // 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 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); +} + +inline simdjson_result> array::at_path_with_wildcard(std::string_view json_path) noexcept { + std::vector result; + + 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; + // Wildcard case + if(key=="*"){ + for(auto element: *this){ + + if(element.error()){ + return element.error(); + } + + if(remaining_path.empty()){ + // Use value_unsafe() because we've already checked for errors above. + // The 'element' is a simdjson_result wrapper, and we need to extract + // the underlying value. value_unsafe() is safe here because error() returned false. + result.push_back(std::move(element).value_unsafe()); + + }else{ + auto nested_result = element.at_path_with_wildcard(remaining_path); + + if(nested_result.error()){ + return nested_result.error(); + } + // Same logic as above. + std::vector nested_matches = std::move(nested_result).value_unsafe(); + + result.insert(result.end(), + std::make_move_iterator(nested_matches.begin()), + std::make_move_iterator(nested_matches.end())); + } + } + return result; + }else{ + // Specific index case in which we access the element at the given index + size_t idx=0; + + for(char c:key){ + if(c < '0' || c > '9'){ + return INVALID_JSON_POINTER; + } + idx = idx*10 + (c - '0'); + } + + auto element = at(idx); + + if(element.error()){ + return element.error(); + } + + if(remaining_path.empty()){ + result.push_back(std::move(element).value_unsafe()); + return result; + }else{ + return element.at_path_with_wildcard(remaining_path); + } + } +} + +simdjson_inline simdjson_result array::at(size_t index) noexcept { + 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::simdjson_result( + icelake::ondemand::array &&value +) noexcept + : implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept + : implementation_simdjson_result_base(error) +{ +} + +simdjson_inline simdjson_result simdjson_result::begin() noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() noexcept { + if (error()) { return error(); } + return first.end(); +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::is_empty() & noexcept { + if (error()) { return error(); } + return first.is_empty(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path(json_path); +} +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path_with_wildcard(json_path); +} +simdjson_inline simdjson_result simdjson_result::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} +{} + +simdjson_inline simdjson_result 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::simdjson_result( + icelake::ondemand::array_iterator &&value +) noexcept + : icelake::implementation_simdjson_result_base(std::forward(value)) +{ + first.iter.assert_is_valid(); +} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : icelake::implementation_simdjson_result_base({}, error) +{ +} + +simdjson_inline simdjson_result simdjson_result::operator*() noexcept { + if (error()) { return error(); } + return *first; +} +simdjson_inline bool simdjson_result::operator==(const simdjson_result &other) const noexcept { + if (!first.iter.is_valid()) { return !error(); } + return first == other.first; +} +simdjson_inline bool simdjson_result::operator!=(const simdjson_result &other) const noexcept { + if (!first.iter.is_valid()) { return error(); } + return first != other.first; +} +simdjson_inline simdjson_result &simdjson_result::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::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 */ + +namespace simdjson { +namespace icelake { +namespace ondemand { + +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 value::get_array() noexcept { + return array::start(iter); +} +simdjson_inline simdjson_result value::get_object() noexcept { + return object::start(iter); +} +simdjson_inline simdjson_result value::start_or_resume_object() noexcept { + if (iter.at_start()) { + return get_object(); + } else { + return object::resume(iter); + } +} + +simdjson_inline simdjson_result value::get_raw_json_string() noexcept { + return iter.get_raw_json_string(); +} +simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { + return iter.get_string(allow_replacement); +} +template +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 value::get_wobbly_string() noexcept { + return iter.get_wobbly_string(); +} +simdjson_inline simdjson_result value::get_double() noexcept { + return iter.get_double(); +} +simdjson_inline simdjson_result value::get_double_in_string() noexcept { + return iter.get_double_in_string(); +} +simdjson_inline simdjson_result value::get_uint64() noexcept { + return iter.get_uint64(); +} +simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { + return iter.get_uint64_in_string(); +} +simdjson_inline simdjson_result value::get_int64() noexcept { + return iter.get_int64(); +} +simdjson_inline simdjson_result value::get_int64_in_string() noexcept { + return iter.get_int64_in_string(); +} +simdjson_inline simdjson_result value::get_bool() noexcept { + return iter.get_bool(); +} +simdjson_inline simdjson_result value::is_null() noexcept { + return iter.is_null(); +} + +template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result 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); } +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(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(bool& out) noexcept { return get_bool().get(out); } + +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline value::operator T() noexcept(false) { + return get(); +} +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 value::begin() & noexcept { + return get_array().begin(); +} +simdjson_inline simdjson_result value::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result value::count_elements() & noexcept { + simdjson_result 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 value::count_fields() & noexcept { + simdjson_result answer; + auto a = get_object(); + answer = a.count_fields(); + iter.move_at_start(); + return answer; +} +simdjson_inline simdjson_result value::at(size_t index) noexcept { + auto a = get_array(); + return a.at(index); +} + +simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result value::find_field(const char *key) noexcept { + return start_or_resume_object().find_field(key); +} + +simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result value::find_field_unordered(const char *key) noexcept { + return start_or_resume_object().find_field_unordered(key); +} + +simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { + return start_or_resume_object()[key]; +} +simdjson_inline simdjson_result value::operator[](const char *key) noexcept { + return start_or_resume_object()[key]; +} + +simdjson_inline simdjson_result value::type() noexcept { + return iter.type(); +} + +simdjson_inline simdjson_result 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 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 value::is_integer() noexcept { + return iter.is_integer(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { + return iter.get_number_type(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { + return iter.get_number(); +} + +simdjson_inline std::string_view value::raw_json_token() noexcept { + return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); +} + +simdjson_inline simdjson_result 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 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::at_pointer(std::string_view json_pointer) noexcept { + 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::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; + } +} + +inline simdjson_result> value::at_path_with_wildcard(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_with_wildcard(json_path); + case json_type::object: + return (*this).get_object().at_path_with_wildcard(json_path); + default: + return INVALID_JSON_POINTER; + } +} + +} // namespace ondemand +} // namespace icelake +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + icelake::ondemand::value &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base(error) +{ +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + if (error()) { return error(); } + return {}; +} + +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) noexcept { + if (error()) { return error(); } + return first.find_field(key); +} + +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} + +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) noexcept { + if (error()) { return error(); } + return first[key]; +} + +simdjson_inline simdjson_result simdjson_result::get_array() noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} + +template<> simdjson_inline error_code simdjson_result::get(icelake::ondemand::value &out) noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} + +template simdjson_inline simdjson_result simdjson_result::get() noexcept { + if (error()) { return error(); } + return first.get(); +} +template simdjson_inline error_code simdjson_result::get(T &out) noexcept { + if (error()) { return error(); } + return first.get(out); +} + +template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { + if (error()) { return error(); } + return std::move(first); +} + +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline simdjson_result::operator T() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first.get(); +} +simdjson_inline simdjson_result::operator icelake::ondemand::array() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator icelake::ondemand::object() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator icelake::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { + if (error()) { return error(); } + return first.raw_json(); +} + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { + if (error()) { return error(); } + return first.current_depth(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer( + std::string_view json_pointer) noexcept { + if (error()) { + return error(); + } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + +inline simdjson_result> simdjson_result::at_path_with_wildcard( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path_with_wildcard(json_path); +} + +} // 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 */ + +namespace simdjson { +namespace icelake { +namespace ondemand { + +simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept + : iter{std::forward(_iter)} +{ + logger::log_start_value(iter, "document"); +} + +simdjson_inline document document::start(json_iterator &&iter) noexcept { + return document(std::forward(iter)); +} + +inline void document::rewind() noexcept { + iter.rewind(); +} + +inline std::string document::to_debug_string() noexcept { + return iter.to_string(); +} + +inline simdjson_result 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 document::start_or_resume_object() noexcept { + if (iter.at_root()) { + return get_object(); + } else { + return object::resume(resume_value_iterator()); + } +} +simdjson_inline simdjson_result document::get_value() noexcept { + // Make sure we start any arrays or objects before returning, so that start_root_() + // 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 document::get_array() & noexcept { + auto value = get_root_value_iterator(); + return array::start_root(value); +} +simdjson_inline simdjson_result 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 document::get_uint64() noexcept { + return get_root_value_iterator().get_root_uint64(true); +} +simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { + return get_root_value_iterator().get_root_uint64_in_string(true); +} +simdjson_inline simdjson_result document::get_int64() noexcept { + return get_root_value_iterator().get_root_int64(true); +} +simdjson_inline simdjson_result document::get_int64_in_string() noexcept { + return get_root_value_iterator().get_root_int64_in_string(true); +} +simdjson_inline simdjson_result document::get_double() noexcept { + return get_root_value_iterator().get_root_double(true); +} +simdjson_inline simdjson_result document::get_double_in_string() noexcept { + return get_root_value_iterator().get_root_double_in_string(true); +} +simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { + return get_root_value_iterator().get_root_string(true, allow_replacement); +} +template +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 document::get_wobbly_string() noexcept { + return get_root_value_iterator().get_root_wobbly_string(true); +} +simdjson_inline simdjson_result document::get_raw_json_string() noexcept { + return get_root_value_iterator().get_root_raw_json_string(true); +} +simdjson_inline simdjson_result document::get_bool() noexcept { + return get_root_value_iterator().get_root_bool(true); +} +simdjson_inline simdjson_result document::is_null() noexcept { + return get_root_value_iterator().is_root_null(true); +} + +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result 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); } +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(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(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 document::get() && noexcept { return get_raw_json_string(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_value(); } + +#if SIMDJSON_EXCEPTIONS +template +simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } +template +simdjson_inline document::operator T() & noexcept(false) { return get(); } +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 document::count_elements() & noexcept { + auto a = get_array(); + simdjson_result 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 document::count_fields() & noexcept { + auto a = get_object(); + simdjson_result 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 document::at(size_t index) & noexcept { + auto a = get_array(); + return a.at(index); +} +simdjson_inline simdjson_result document::begin() & noexcept { + return get_array().begin(); +} +simdjson_inline simdjson_result document::end() & noexcept { + return {}; +} + +simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field(const char *key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::find_field_unordered(const char *key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { + return start_or_resume_object()[key]; +} +simdjson_inline simdjson_result 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 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(starting_point), size_t(final_point - starting_point)); +} + +simdjson_inline simdjson_result document::type() noexcept { + return get_root_value_iterator().type(); +} + +simdjson_inline simdjson_result 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 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 document::is_integer() noexcept { + return get_root_value_iterator().is_root_integer(true); +} + +simdjson_inline simdjson_result document::get_number_type() noexcept { + return get_root_value_iterator().get_root_number_type(true); +} + +simdjson_inline simdjson_result document::get_number() noexcept { + return get_root_value_iterator().get_root_number(true); +} + + +simdjson_inline simdjson_result document::raw_json_token() noexcept { + auto _iter = get_root_value_iterator(); + return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); +} + +simdjson_inline simdjson_result 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 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; + } +} + +simdjson_inline simdjson_result> document::at_path_with_wildcard(std::string_view json_path) noexcept { + rewind(); // Rewind the document each time 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().at_path_with_wildcard(json_path); + case json_type::object: + return (*this).get_object().at_path_with_wildcard(json_path); + default: + return INVALID_JSON_POINTER; + } +} + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (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) { + // 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::simdjson_result( + icelake::ondemand::document &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base( + error + ) +{ +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { + if (error()) { return error(); } + return first.get_value(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} + +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} + +template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; +template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first); +} +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(icelake::ondemand::document &out) & noexcept = delete; +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(icelake::ondemand::document &out) && noexcept { + if (error()) { return error(); } + out = std::forward(first); + return SUCCESS; +} + +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} + +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} + +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} + +simdjson_inline bool simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} + +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} + +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} + +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} + + +#if SIMDJSON_EXCEPTIONS +template ::value == false>::type> +simdjson_inline simdjson_result::operator T() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator icelake::ondemand::array() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator icelake::ondemand::object() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator icelake::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator icelake::ondemand::value() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline bool simdjson_result::at_end() const noexcept { + if (error()) { return error(); } + return first.at_end(); +} + + +simdjson_inline int32_t simdjson_result::current_depth() const noexcept { + if (error()) { return error(); } + return first.current_depth(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path(json_path); +} + +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path_with_wildcard(json_path); +} + +#if SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(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 document_reference::get_array() & noexcept { return doc->get_array(); } +simdjson_inline simdjson_result 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 document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } +simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } +simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } +template +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 document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } +simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } +simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } +simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } +simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline document_reference::operator T() noexcept(false) { return get(); } +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 document_reference::count_elements() & noexcept { return doc->count_elements(); } +simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } +simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } +simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } +simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } +simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } +simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } +simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } +simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } +simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } +simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } +simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } +simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } +simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } +simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } +simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } +simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } +simdjson_inline simdjson_result> document_reference::at_path_with_wildcard(std::string_view json_path) noexcept { return doc->at_path_with_wildcard(json_path); } +simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} +simdjson_inline document_reference::operator document&() const noexcept { return *doc; } +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code document_reference::extract_into(T& out) & noexcept { + return doc->extract_into(out); +} +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION +} // namespace ondemand +} // namespace icelake +} // namespace simdjson + + + +namespace simdjson { +simdjson_inline simdjson_result::simdjson_result(icelake::ondemand::document_reference value, error_code error) + noexcept : implementation_simdjson_result_base(std::forward(value), error) {} + + +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { + if (error()) { return error(); } + return first.get_value(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} +template <> +simdjson_warn_unused simdjson_inline error_code simdjson_result::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::get(icelake::ondemand::document_reference &out) && noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline simdjson_result::operator T() noexcept(false) { + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + if (error()) { throw simdjson_error(error()); } + return first.get(); +} +simdjson_inline simdjson_result::operator icelake::ondemand::array() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator icelake::ondemand::object() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator icelake::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator icelake::ondemand::value() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path_with_wildcard(json_path); +} +#if SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(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 +#include + +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 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 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 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 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 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 +) 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}, + 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}, + allow_comma_separated{false}, + 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 { + 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 document_stream::iterator::operator*() noexcept { + return simdjson_result(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; } + doc_index = batch_start; + 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 + 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(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; + } + } +} + +inline void document_stream::next_document() noexcept { + // 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(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; + if (remaining <= batch_size) { + return p.implementation->stage1(&buf[_batch_start], remaining, stage1_mode::streaming_final); + } else { + return p.implementation->stage1(&buf[_batch_start], batch_size, stage1_mode::streaming_partial); + } +} + +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 { + auto depth = stream->doc.iter.depth(); + auto cur_struct_index = stream->doc.iter._root - stream->parser->implementation->structural_indexes.get(); + + // If at root, process the first token to determine if scalar value + if (stream->doc.iter.at_root()) { + 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 + // TODO: We could remove trailing whitespaces + // This returns a string spanning from start of value to the beginning of the next document (excluded) + { + auto next_index = 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(stream->buf) + current_index(); + while(svlen > 1 && (std::isspace(start[svlen-1]) || start[svlen-1] == '\0')) { + svlen--; + } + return std::string_view(start, svlen); + } + } + cur_struct_index++; + } + + while (cur_struct_index <= static_cast(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(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::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base(error) +{ +} +simdjson_inline simdjson_result::simdjson_result( + icelake::ondemand::document_stream &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(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() {} + +simdjson_inline field::field(raw_json_string key, ondemand::value &&value) noexcept + : std::pair(key, std::forward(value)) +{ +} + +simdjson_inline simdjson_result 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::start(const value_iterator &parent_iter, raw_json_string key) noexcept { + return field(key, parent_iter.child()); +} + +simdjson_inline simdjson_warn_unused simdjson_result 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 answer = first.unescape(second.iter.json_iter(), allow_replacement); + first.consume(); + return answer; +} + +template +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) ); + 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(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(first.buf), end_quote - first.buf); +} + +simdjson_inline value &field::value() & noexcept { + return second; +} + +simdjson_inline value field::value() && noexcept { + return std::forward(*this).second; +} + +} // namespace ondemand +} // namespace icelake +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + icelake::ondemand::field &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base(error) +{ +} + +simdjson_inline simdjson_result simdjson_result::key() noexcept { + if (error()) { return error(); } + return first.key(); +} + +simdjson_inline simdjson_result simdjson_result::key_raw_json_token() noexcept { + if (error()) { return error(); } + return first.key_raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::escaped_key() noexcept { + if (error()) { return error(); } + return first.escaped_key(); +} + +simdjson_inline simdjson_result simdjson_result::unescaped_key(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.unescaped_key(allow_replacement); +} + +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::unescaped_key(string_type &receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.unescaped_key(receiver, allow_replacement); +} + +simdjson_inline simdjson_result simdjson_result::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(other.token)), + parser{other.parser}, + _string_buf_loc{other._string_buf_loc}, + error{other.error}, + _depth{other._depth}, + _root{other._root}, + _streaming{other._streaming} +{ + 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; + 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} + +{ + 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 '"': + if(*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(TAPE_ERROR, "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; +} + +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(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 json_iterator::current_location() const noexcept { + if (!is_alive()) { // Unrecoverable error + if (!at_root()) { + return reinterpret_cast(token.peek(-1)); + } else { + return reinterpret_cast(token.peek()); + } + } + if (at_end()) { + return OUT_OF_BOUNDS; + } + return reinterpret_cast(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 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 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::simdjson_result(icelake::ondemand::json_iterator &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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; + default: SIMDJSON_UNREACHABLE(); + } + return out; +} + +#if SIMDJSON_EXCEPTIONS +inline std::ostream& operator<<(std::ostream& out, simdjson_result &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::simdjson_result(icelake::ondemand::json_type &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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 +#include + +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 +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_s); + if (size <= 0) return std::string(); + std::unique_ptr 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 +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)...); +} + +template +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)...); + 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): #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 */ +/* amalgamation skipped (editor-only): #endif */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace icelake { +namespace ondemand { + +simdjson_inline simdjson_result 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} +simdjson_inline simdjson_result 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} +simdjson_inline simdjson_result object::operator[](const std::string_view key) & noexcept { + return find_field_unordered(key); +} +simdjson_inline simdjson_result object::operator[](const std::string_view key) && noexcept { + return std::forward(*this).find_field_unordered(key); +} +simdjson_inline simdjson_result object::find_field(const std::string_view key) & noexcept { + 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} +simdjson_inline simdjson_result object::find_field(const std::string_view key) && noexcept { + 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} + +simdjson_inline simdjson_result object::start(value_iterator &iter) noexcept { + SIMDJSON_TRY( iter.start_object().error() ); + return object(iter); +} +simdjson_inline simdjson_result 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 object::raw_json() noexcept { + 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(starting_point), size_t(final_point - starting_point)); +} + +simdjson_inline simdjson_result 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::begin() noexcept { +#if SIMDJSON_DEVELOPMENT_CHECKS + if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } +#endif + return object_iterator(iter); +} +simdjson_inline simdjson_result object::end() noexcept { + return object_iterator(iter); +} + +inline simdjson_result object::at_pointer(std::string_view json_pointer) noexcept { + if (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 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 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); +} + +inline simdjson_result> object::at_path_with_wildcard(std::string_view json_path) noexcept { + std::vector result; + + 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; + // 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()) { + result.push_back(std::move(val)); + } else { + auto nested_result = val.at_path_with_wildcard(remaining_path); + + if (nested_result.error()) { + return nested_result.error(); + } + // Extract and append all nested matches to our result + std::vector nested_vec; + SIMDJSON_TRY(std::move(nested_result).get(nested_vec)); + + result.insert(result.end(), + std::make_move_iterator(nested_vec.begin()), + std::make_move_iterator(nested_vec.end())); + } + } + return result; + } else { + value val; + SIMDJSON_TRY(find_field(key).get(val)); + + if (remaining_path.empty()) { + result.push_back(std::move(val)); + return result; + } else { + return val.at_path_with_wildcard(remaining_path); + } + } +} + +simdjson_inline simdjson_result 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 object::is_empty() & noexcept { + 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 object::reset() & noexcept { + return iter.reset_object(); +} + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (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) { + // 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::simdjson_result(icelake::ondemand::object &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(error) {} + +simdjson_inline simdjson_result simdjson_result::begin() noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() noexcept { + if (error()) { return error(); } + return first.end(); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) && noexcept { + if (error()) { return error(); } + return std::forward(first).find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) && noexcept { + if (error()) { return error(); } + return std::forward(first)[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) && noexcept { + if (error()) { return error(); } + return std::forward(first).find_field(key); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path_with_wildcard(json_path); +} + +inline simdjson_result simdjson_result::reset() noexcept { + if (error()) { return error(); } + return first.reset(); +} + +inline simdjson_result simdjson_result::is_empty() noexcept { + if (error()) { return error(); } + return first.is_empty(); +} + +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} + +simdjson_inline simdjson_result simdjson_result::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} +{} + +simdjson_inline simdjson_result 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::simdjson_result( + icelake::ondemand::object_iterator &&value +) noexcept + : implementation_simdjson_result_base(std::forward(value)) +{ + first.iter.assert_is_valid(); +} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base({}, error) +{ +} + +simdjson_inline simdjson_result simdjson_result::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::operator==(const simdjson_result &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::operator!=(const simdjson_result &other) const noexcept { + if (!first.iter.is_valid()) { return error(); } + return first != other.first; +} +// Checks for ']' and ',' +simdjson_inline simdjson_result &simdjson_result::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/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; + 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 parser::iterate(padded_string_view json) & noexcept { + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } + + json.remove_utf8_bom(); + + // 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(json.data()), json.length(), stage1_mode::regular) ); + return document::start({ reinterpret_cast(json.data()), this }); +} + +#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept { + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } + + json.remove_utf8_bom(); + + // 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(json.data()), json.length(), stage1_mode::regular); + if (err) { + if (err != UNCLOSED_STRING) + return err; + } + return document::start({ reinterpret_cast(json.data()), this, true }); +} +#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON + +simdjson_warn_unused simdjson_inline simdjson_result 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 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 parser::iterate(std::string_view json, size_t allocated) & noexcept { + return iterate(padded_string_view(json, allocated)); +} + +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(std::string &json) & noexcept { + return iterate(pad_with_reserve(json)); +} + +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const std::string &json) & noexcept { + return iterate(padded_string_view(json)); +} + +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const simdjson_result &result) & noexcept { + // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 parser::iterate(const simdjson_result &result) & noexcept { + // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 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(json.data()), json.length(), stage1_mode::regular) ); + return json_iterator(reinterpret_cast(json.data()), this); +} + +inline simdjson_result 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; + } + if(allow_comma_separated && batch_size < len) { batch_size = len; } + return document_stream(*this, buf, len, batch_size, allow_comma_separated); +} + +inline simdjson_result 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(buf), len, batch_size, allow_comma_separated); +} +inline simdjson_result 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 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 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 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_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 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(dst), end-dst); + dst = end; + return result; +} + +simdjson_inline simdjson_warn_unused simdjson_result 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(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 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& parser::get_parser_instance() { + std::unique_ptr& 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& 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 parser_instance = nullptr; + return parser_instance; +} + + +} // namespace ondemand +} // namespace icelake +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result(icelake::ondemand::parser &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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(buf); +} + +simdjson_inline char raw_json_string::operator[](size_t i) const noexcept { + return reinterpret_cast(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()); + } + const char * r{raw()}; + size_t pos{0}; + for(;pos < target.size();pos++) { + if(r[pos] != target[pos]) { return false; } + } + if(r[pos] != '"') { return false; } + return true; +} + +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 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 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::simdjson_result(icelake::ondemand::raw_json_string &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(error) {} + +simdjson_inline simdjson_result simdjson_result::raw() const noexcept { + if (error()) { return error(); } + return first.raw(); +} +simdjson_inline char simdjson_result::operator[](size_t i) const noexcept { + if (error()) { return error(); } + return first[i]; +} +simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::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 simdjson_result::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::simdjson_result(icelake::ondemand::token_iterator &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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 { + +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 value_iterator::start_object() noexcept { + SIMDJSON_TRY( start_container('{', "Not an object", "object") ); + return started_object(); +} + +simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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 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 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_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 value_iterator::start_array() noexcept { + SIMDJSON_TRY( start_container('[', "Not an array", "array") ); + return started_array(); +} + +simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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(!not_true); +} +simdjson_warn_unused simdjson_inline simdjson_result 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 value_iterator::get_string(bool allow_replacement) noexcept { + return get_raw_json_string().unescape(json_iter(), allow_replacement); +} +template +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; } + 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 value_iterator::get_wobbly_string() noexcept { + return get_raw_json_string().unescape_wobbly(json_iter()); +} +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_raw_json_string() noexcept { + auto json = peek_scalar("string"); + if (*json != '"') { return incorrect_type_error("Not a string"); } + advance_scalar("string"); + return raw_json_string(json+1); +} +simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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 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 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 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 value_iterator::is_integer() noexcept { + return numberparsing::is_integer(peek_non_root_scalar("integer")); +} +simdjson_inline simdjson_result value_iterator::get_number_type() noexcept { + return numberparsing::get_number_type(peek_non_root_scalar("integer")); +} +simdjson_inline simdjson_result 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 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 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.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 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.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 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 +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; } + 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 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 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; } + advance_scalar("string"); + return raw_json_string(json+1); +} +simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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.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 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.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 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 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 simdjson_result value_iterator::reset_array() noexcept { + if(error()) { return error(); } + move_at_container_start(); + return started_array(); +} + +simdjson_inline simdjson_result 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 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::simdjson_result(icelake::ondemand::value_iterator &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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 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 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 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 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 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 to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result 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 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 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&& x) { + if (x.error()) { throw simdjson::simdjson_error(x.error()); } + return (out << x.value()); +} +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& 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 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 emails; }; + * + * std::string email; + * path_accessor::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 +#include +#include + +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 +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 +consteval auto make_field_step(const char (&name)[N]) { + return path_step(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 +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 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 +struct path_accessor { + using value = ::simdjson::icelake::ondemand::value; + + static constexpr auto parser = json_path_parser(); + 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 + static inline simdjson_result access(DocOrValue& doc_or_val) noexcept { + // Validate path at compile time if T is a struct + if constexpr (std::is_class_v) { + 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(doc_or_val.get_value()); + } + + // Extract value at path directly into target with compile-time type validation + // Example: std::string name; path_accessor::extract_field(doc, name); + template + static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept { + static_assert(std::is_class_v, "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(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 + static consteval std::enable_if_t, 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 + static inline error_code extract_with_reflection(simdjson_result 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(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(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_value, target_ref); + } + } + } + // Skip unexpected characters and continue + else { + return extract_with_reflection(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 + static inline simdjson_result access_impl(simdjson_result 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_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_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_value); + } + } else { + return access_impl(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(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 + 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 + 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 + static consteval bool is_container_type() { + using BaseType = std::remove_cvref_t; + if constexpr (requires { typename BaseType::value_type; }) { + return true; + } + if constexpr (std::is_array_v) { + return true; + } + return false; + } + + // Extract element type from non-reflected container + template + using extract_element_type = std::conditional_t< + requires { typename std::remove_cvref_t::value_type; }, + typename std::remove_cvref_t::value_type, + std::conditional_t< + std::is_array_v>, + std::remove_extent_t>, + void + > + >; + + // Validate path matches struct definition + static consteval bool validate_path() { + if constexpr (!std::is_class_v) { + 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 +inline simdjson_result<::simdjson::icelake::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = path_accessor; + return accessor::access(doc_or_val); +} + +// Overload without type parameter (no validation) +template +inline simdjson_result<::simdjson::icelake::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = path_accessor; + return accessor::access(doc_or_val); +} + +// ============================================================================ +// JSON Pointer Compile-Time Support (RFC 6901) +// ============================================================================ + +// JSON Pointer parser: /field/0/nested (slash-separated) +template +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 +struct pointer_accessor { + using parser = json_pointer_parser; + 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) { + 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::is_array_like_reflected(current_type)) { + return false; + } + current_type = path_accessor::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 + static inline simdjson_result access_impl(simdjson_result 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(next_value); + } else { + auto obj = current.get_object().value_unsafe(); + auto next_value = obj.find_field_unordered(token); + return access_impl(next_value); + } + } + } + + // Access JSON value at pointer + template + static inline simdjson_result access(DocOrValue& doc_or_val) noexcept { + if constexpr (std::is_class_v) { + 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 current = doc_or_val.get_value(); + return access_impl<0>(current); + } + + // Extract value at pointer directly into target with type validation + template + static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept { + static_assert(std::is_class_v, "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 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 + static consteval std::enable_if_t, 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::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 +inline simdjson_result<::simdjson::icelake::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = pointer_accessor; + return accessor::access(doc_or_val); +} + +// Overload without type parameter (no validation) +template +inline simdjson_result<::simdjson::icelake::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = pointer_accessor; + 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 struct simd8; +template 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 + +// 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 + +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { +#if SIMDJSON_REGULAR_VISUAL_STUDIO + *result = value1 + value2; + return *result < value1; +#else + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); +#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 + +#if defined(__linux__) +#include +#elif defined(__FreeBSD__) +#include +#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 + +namespace simdjson { +namespace ppc64 { +namespace { +namespace simd { + +using __m128i = __vector unsigned char; + +template 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(this); + *this_cast = *this_cast | other; + return *this_cast; + } + simdjson_inline Child &operator&=(const Child other) { + auto this_cast = static_cast(this); + *this_cast = *this_cast & other; + return *this_cast; + } + simdjson_inline Child &operator^=(const Child other) { + auto this_cast = static_cast(this); + *this_cast = *this_cast ^ other; + return *this_cast; + } +}; + +template > +struct base8 : base> { + typedef uint16_t bitmask_t; + typedef uint32_t bitmask2_t; + + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m128i _value) : base>(_value) {} + + friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { + return (__m128i)vec_cmpeq(lhs.value, (__m128i)rhs); + } + + static const int SIZE = sizeof(base>::value); + + template + simdjson_inline simd8 prev(simd8 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 : base8 { + static simdjson_inline simd8 splat(bool _value) { + return (__m128i)vec_splats((unsigned char)(-(!!_value))); + } + + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m128i _value) + : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) + : base8(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(result[1]); +#else + return static_cast(result[0]); +#endif + } + simdjson_inline bool any() const { + return !vec_all_eq(this->value, (__m128i)vec_splats(0)); + } + simdjson_inline simd8 operator~() const { + return this->value ^ (__m128i)splat(true); + } +}; + +template struct base8_numeric : base8 { + static simdjson_inline simd8 splat(T value) { + (void)value; + return (__m128i)vec_splats(value); + } + static simdjson_inline simd8 zero() { return splat(0); } + static simdjson_inline simd8 load(const T values[16]) { + return (__m128i)(vec_vsx_ld(0, reinterpret_cast(values))); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, + v14, v15); + } + + simdjson_inline base8_numeric() : base8() {} + simdjson_inline base8_numeric(const __m128i _value) + : base8(_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 operator~() const { return *this ^ 0xFFu; } + + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { + return (__m128i)((__m128i)this->value + (__m128i)other); + } + simdjson_inline simd8 operator-(const simd8 other) const { + return (__m128i)((__m128i)this->value - (__m128i)other); + } + simdjson_inline simd8 &operator+=(const simd8 other) { + *this = *this + other; + return *static_cast *>(this); + } + simdjson_inline simd8 &operator-=(const simd8 other) { + *this = *this - other; + return *static_cast *>(this); + } + + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior + // for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 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 compress(uint32_t mask) + // would be sensible, but the AVX ISA makes this kind of approach difficult. + template + 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(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 + simdjson_inline simd8 + 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::repeat_16( + replace0, replace1, replace2, replace3, replace4, replace5, replace6, + replace7, replace8, replace9, replace10, replace11, replace12, + replace13, replace14, replace15)); + } +}; + +// Signed bytes +template <> struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) + : base8_numeric(_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 + 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(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, + v13, v14, v15); + } + + // Order-sensitive comparisons + simdjson_inline simd8 + max_val(const simd8 other) const { + return (__m128i)vec_max((__vector signed char)this->value, + (__vector signed char)(__m128i)other); + } + simdjson_inline simd8 + min_val(const simd8 other) const { + return (__m128i)vec_min((__vector signed char)this->value, + (__vector signed char)(__m128i)other); + } + simdjson_inline simd8 + operator>(const simd8 other) const { + return (__m128i)vec_cmpgt((__vector signed char)this->value, + (__vector signed char)(__m128i)other); + } + simdjson_inline simd8 + operator<(const simd8 other) const { + return (__m128i)vec_cmplt((__vector signed char)this->value, + (__vector signed char)(__m128i)other); + } +}; + +// Unsigned bytes +template <> struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) + : base8_numeric(_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 + 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(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, + v13, v14, v15); + } + + // Saturated math + simdjson_inline simd8 + saturating_add(const simd8 other) const { + return (__m128i)vec_adds(this->value, (__m128i)other); + } + simdjson_inline simd8 + saturating_sub(const simd8 other) const { + return (__m128i)vec_subs(this->value, (__m128i)other); + } + + // Order-specific operations + simdjson_inline simd8 + max_val(const simd8 other) const { + return (__m128i)vec_max(this->value, (__m128i)other); + } + simdjson_inline simd8 + min_val(const simd8 other) const { + return (__m128i)vec_min(this->value, (__m128i)other); + } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 + gt_bits(const simd8 other) const { + return this->saturating_sub(other); + } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 + lt_bits(const simd8 other) const { + return other.saturating_sub(*this); + } + simdjson_inline simd8 + operator<=(const simd8 other) const { + return other.max_val(*this) == other; + } + simdjson_inline simd8 + operator>=(const simd8 other) const { + return other.min_val(*this) == other; + } + simdjson_inline simd8 + operator>(const simd8 other) const { + return this->gt_bits(other).any_bits_set(); + } + simdjson_inline simd8 + operator<(const simd8 other) const { + return this->gt_bits(other).any_bits_set(); + } + + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { + return (__m128i)vec_cmpeq(this->value, (__m128i)vec_splats(uint8_t(0))); + } + simdjson_inline simd8 bits_not_set(simd8 bits) const { + return (*this & bits).bits_not_set(); + } + simdjson_inline simd8 any_bits_set() const { + return ~this->bits_not_set(); + } + simdjson_inline simd8 any_bits_set(simd8 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 bits) const { + return vec_all_eq(vec_and(this->value, (__m128i)bits), + (__m128i)vec_splats(0)); + } + simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { + return !bits_not_set_anywhere(bits); + } + template simdjson_inline simd8 shr() const { + return simd8( + (__m128i)vec_sr(this->value, (__m128i)vec_splat_u8(N))); + } + template simdjson_inline simd8 shl() const { + return simd8( + (__m128i)vec_sl(this->value, (__m128i)vec_splat_u8(N))); + } +}; + +template struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 4, + "PPC64 kernel should use four registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } + + simd8x64(const simd8x64 &o) = delete; // no copy allowed + simd8x64 & + operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed + + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, + const simd8 chunk2, const simd8 chunk3) + : chunks{chunk0, chunk1, chunk2, chunk3} {} + simdjson_inline simd8x64(const T ptr[64]) + : chunks{simd8::load(ptr), simd8::load(ptr + 16), + simd8::load(ptr + 32), simd8::load(ptr + 48)} {} + + simdjson_inline void store(T ptr[64]) const { + this->chunks[0].store(ptr + sizeof(simd8) * 0); + this->chunks[1].store(ptr + sizeof(simd8) * 1); + this->chunks[2].store(ptr + sizeof(simd8) * 2); + this->chunks[3].store(ptr + sizeof(simd8) * 3); + } + + simdjson_inline simd8 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 mask = simd8::splat(m); + return simd8x64(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 &other) const { + return simd8x64(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 mask = simd8::splat(m); + return simd8x64(this->chunks[0] <= mask, this->chunks[1] <= mask, + this->chunks[2] <= mask, this->chunks[3] <= mask) + .to_bitmask(); + } +}; // struct simd8x64 + +} // 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 v0(src); + simd8 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(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 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 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_BUILDER_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; + +} // 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 struct is_builtin_deserializable : std::false_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; + +template +concept is_builtin_deserializable_v = is_builtin_deserializable::value; + +template +concept custom_deserializable = tag_invocable; + +template +concept deserializable = custom_deserializable || is_builtin_deserializable_v || concepts::optional_type; + +template +concept nothrow_custom_deserializable = nothrow_tag_invocable; + +// built-in types are noexcept and if an error happens, the value simply gets ignored and the error is returned. +template +concept nothrow_deserializable = nothrow_custom_deserializable || is_builtin_deserializable_v; + +/// 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 + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(array_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for object + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(object_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for value + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document reference + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + 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 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 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 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 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 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 has_next_field() noexcept; + + /** + * Get the current field's key. + */ + simdjson_warn_unused simdjson_inline simdjson_result field_key() 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 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 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 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 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 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 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 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 get_string(bool allow_replacement) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_int64() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_double() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_bool() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result is_null() noexcept; + simdjson_warn_unused simdjson_inline bool is_negative() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result is_integer() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_number() noexcept; + + simdjson_warn_unused simdjson_inline simdjson_result get_root_string(bool check_trailing, bool allow_replacement) noexcept; + template + 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 get_root_wobbly_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_raw_json_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64_in_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_int64(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_int64_in_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_double(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_double_in_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_bool(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline bool is_root_negative() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result is_root_integer(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_number_type(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_number(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result 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 reset_array() noexcept; + /** + * Restarts an object iteration. + * @returns Whether the object has any fields (returns false for empty). + */ + simdjson_inline simdjson_result 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 parse_null(const uint8_t *json) const noexcept; + simdjson_inline simdjson_result 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 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; + /** @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 : public ppc64::implementation_simdjson_result_base { +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 */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include + +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, uint64_t, int64_t, double, bool + * + * You may use get_double(), get_bool(), get_uint64(), get_int64(), + * 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 + simdjson_inline simdjson_result get() +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + #if SIMDJSON_SUPPORTS_CONCEPTS + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else if constexpr (concepts::optional_type) { + using value_type = typename std::remove_cvref_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(out.value()); + } else { + static_assert(!sizeof(T), "The get 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 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, double, or bool."); + static_cast(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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " 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(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 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 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 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 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 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 get_int64_in_string() 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 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 get_double_in_string() noexcept; + + /** + * Cast this JSON value to a string. + * + * The string is guaranteed to be valid UTF-8. + * + * Equivalent to get(). + * + * 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 get_string(bool allow_replacement = false) noexcept; + + /** + * 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 + 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 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 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 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 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 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 + 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 signed 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(). + * + * @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 begin() & noexcept; + /** + * Sentinel representing the end of the array. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result 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 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 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 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 find_field(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ + simdjson_inline simdjson_result 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 find_field_unordered(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result find_field_unordered(const char *key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result 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 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 is_scalar() noexcept; + /** + * Checks whether the value is a string. + * + * @returns true if the type is string + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + */ + simdjson_inline simdjson_result is_string() noexcept; + + /** + * Checks whether the value is a negative number. + * + * @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 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 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 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 raw_json() noexcept; + + /** + * Returns the current location in the document if in bounds. + */ + simdjson_inline simdjson_result 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 at_pointer(std::string_view json_pointer) noexcept; + + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + simdjson_inline simdjson_result at_path(std::string_view at_path) noexcept; + + /** + * Get all values matching the given JSONPath expression with wildcard support. + * Supports wildcard character (*) for arrays or ".*" for objects. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern + */ + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) 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 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; + friend struct simdjson_result; + friend class field; +}; + +} // namespace ondemand +} // namespace ppc64 +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public ppc64::implementation_simdjson_result_base { +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 get_array() noexcept; + simdjson_inline simdjson_result get_object() noexcept; + + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result is_null() noexcept; + + template simdjson_inline simdjson_result get() noexcept; + + template simdjson_inline error_code get(T &out) noexcept; + +#if SIMDJSON_EXCEPTIONS + template + 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 count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result 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 find_field(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ + simdjson_inline simdjson_result 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 find_field_unordered(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result find_field_unordered(const char *key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result 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 type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + simdjson_inline simdjson_result is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + + /** @copydoc simdjson_inline std::string_view value::raw_json_token() const noexcept */ + simdjson_inline simdjson_result raw_json_token() noexcept; + simdjson_inline simdjson_result raw_json() noexcept; + + /** @copydoc simdjson_inline simdjson_result current_location() noexcept */ + simdjson_inline simdjson_result current_location() noexcept; + /** @copydoc simdjson_inline int32_t current_depth() const noexcept */ + simdjson_inline simdjson_result current_depth() const noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) 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 +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 +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 (`{` `}` `[` `]` `,` `:` `""` `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 value_iterator; + friend class object; + template + friend simdjson_inline void logger::log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta, logger::log_level level, Args&&... args) noexcept; + template + 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 : public ppc64::implementation_simdjson_result_base { +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}; + +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; + + /** + * 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 unescape(raw_json_string in, bool allow_replacement) noexcept; + simdjson_inline simdjson_result 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 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 + 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 + 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 : public ppc64::implementation_simdjson_result_base { +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 + 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 + 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 &type) noexcept(false); +#endif + +} // namespace ondemand +} // namespace ppc64 +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public ppc64::implementation_simdjson_result_base { +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 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 unescape_wobbly(json_iterator &iter) const noexcept; + const uint8_t * buf{}; + friend class object; + friend class field; + friend class parser; + friend struct simdjson_result; +}; + +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 : public ppc64::implementation_simdjson_result_base { +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 raw() const noexcept; + simdjson_inline char operator[](size_t) const noexcept; + simdjson_inline simdjson_warn_unused simdjson_result unescape(ppc64::ondemand::json_iterator &iter, bool allow_replacement) const noexcept; + simdjson_inline simdjson_warn_unused simdjson_result 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 +#include + +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 iterate(padded_string_view json) & noexcept; +#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON + simdjson_warn_unused simdjson_result iterate_allow_incomplete_json(padded_string_view json) & noexcept; +#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const char *json, size_t len, size_t capacity) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const uint8_t *json, size_t len, size_t capacity) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(std::string_view json, size_t capacity) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const std::string &json) & noexcept; + /** @overload simdjson_result 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 iterate(std::string &json) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const simdjson_result &json) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const simdjson_result &json) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result 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 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 10MB, which has been a reasonable sweet spot in our tests. + * @param allow_comma_separated (defaults on false) This allows a mode where the documents are + * separated by commas instead of whitespace. It comes with a performance + * penalty because the entire document is indexed at once (and the document must be + * less than 4 GB), and there is no multithreading. In this mode, the batch_size parameter + * is effectively ignored, as it is set to at least the document size. + * @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 iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(padded_string_view json, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_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 iterate_many(std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @private We do not want to allow implicit conversion from C string to std::string. */ + simdjson_result iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete; + + /** 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 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 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: + friend bool release_parser(); + friend ondemand::parser& get_parser(); + /** Get the thread-local parser instance, allocates it if needed */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_parser_instance(); + /** Get the thread-local parser instance, it might be null */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_threadlocal_parser_if_exists(); + /** @private [for benchmarking access] The implementation to use */ + std::unique_ptr implementation{}; + size_t _capacity{0}; + size_t _max_capacity; + size_t _max_depth{DEFAULT_MAX_DEPTH}; + std::unique_ptr string_buf{}; + +#if SIMDJSON_DEVELOPMENT_CHECKS + std::unique_ptr start_positions{}; +#endif + + friend class json_iterator; + friend class document_stream; +}; + +} // namespace ondemand +} // namespace ppc64 +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public ppc64::implementation_simdjson_result_base { +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 */ +/* 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. + */ + simdjson_inline simdjson_result begin() noexcept; + /** + * Sentinel representing the end of the array. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result 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 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 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 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 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 at_path(std::string_view json_path) noexcept; + + /** + * Get all values matching the given JSONPath expression with wildcard support. + * Supports wildcard patterns like "[*]" to match all array elements. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern + */ + inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + + /** + * Consumes the array and returns a string_view instance corresponding to the + * array as represented in JSON. It points inside the original document. + */ + simdjson_inline simdjson_result 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 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + 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 + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(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 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 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 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{}; + + friend class value; + friend class document; + friend struct simdjson_result; + friend struct simdjson_result; + friend class array_iterator; +}; + +} // namespace ondemand +} // namespace ppc64 +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public ppc64::implementation_simdjson_result_base { +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 begin() noexcept; + simdjson_inline simdjson_result end() noexcept; + inline simdjson_result count_elements() & noexcept; + inline simdjson_result is_empty() & noexcept; + inline simdjson_result reset() & noexcept; + simdjson_inline simdjson_result at(size_t index) noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h + + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(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 */ +/* 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; + 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; + + // + // Iterator interface + // + + /** + * Get the current element. + * + * Part of the std::iterator interface. + */ + simdjson_inline simdjson_result + 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}; +#endif + value_iterator iter{}; + + simdjson_inline array_iterator(const value_iterator &iter) noexcept; + + friend class array; + friend class value; + friend struct simdjson_result; +}; + +} // namespace ondemand +} // namespace ppc64 +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public ppc64::implementation_simdjson_result_base { + using iterator_category = std::input_iterator_tag; + using value_type = simdjson_result; + 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 operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline bool operator==(const simdjson_result &) const noexcept; + simdjson_inline bool operator!=(const simdjson_result &) const noexcept; + simdjson_inline simdjson_result &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 */ +/* 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 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 get_object() & noexcept; + /** + * Cast this JSON value to an unsigned integer. + * + * @returns A signed 64-bit integer. + * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. + */ + simdjson_inline simdjson_result get_uint64() noexcept; + /** + * Cast this JSON value (inside string) to an unsigned integer. + * + * @returns A signed 64-bit integer. + * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. + */ + simdjson_inline simdjson_result 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 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 get_int64_in_string() 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 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 get_double_in_string() noexcept; + /** + * 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 get_string(bool allow_replacement = false) noexcept; + /** + * 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 + 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 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 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 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 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 is_null() noexcept; + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool + * + * You may use get_double(), get_bool(), get_uint64(), get_int64(), + * 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 + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + /** + * @overload template simdjson_result 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 + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::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(*this).get(); + } + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_CONCEPTS + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { + static_assert(!sizeof(T), "The get 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 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, double, or bool."); + static_cast(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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " 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(out); // to get rid of unused errors + return UNINITIALIZED; +#endif // SIMDJSON_SUPPORTS_CONCEPTS + } + + /** @overload template error_code get(T &out) & noexcept */ + template simdjson_deprecated simdjson_inline error_code get(T &out) && noexcept; + +#if SIMDJSON_EXCEPTIONS + /** + * Cast this JSON value to an instance of type T. The programmer is responsible for + * providing an implementation of get 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 + explicit simdjson_inline operator T() & noexcept(false); + template + 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 signed 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. + * + * @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) 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. + */ + 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. + */ + 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 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 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 at(size_t index) & noexcept; + /** + * Begin array iteration. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result begin() & noexcept; + /** + * Sentinel representing the end of the array. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result 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 find_field(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result 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 find_field_unordered(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result 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 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 is_scalar() noexcept; + + /** + * Checks whether the document is a string. + * + * @returns true if the type is string + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + */ + simdjson_inline simdjson_result is_string() noexcept; + + /** + * Checks whether the document is a negative number. + * + * @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 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 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 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 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 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 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 at_path(std::string_view json_path) noexcept; + + /** + * Get all values matching the given JSONPath expression with wildcard support. + * + * Supports wildcard patterns like "$.array[*]" or "$.object.*" to match multiple elements. + * + * This method materializes all matching values into a vector. + * The document will be consumed after this call. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern, or: + * - INVALID_JSON_POINTER if the JSONPath cannot be parsed + * - NO_SUCH_FIELD if a field does not exist + * - INDEX_OUT_OF_BOUNDS if an array index is out of bounds + * - INCORRECT_TYPE if path traversal encounters wrong type + */ + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + + /** + * Consumes the document and returns a string_view instance corresponding to the + * document as represented in JSON. It points inside the original byte array containing + * the JSON document. + */ + simdjson_inline simdjson_result 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 + requires(std::is_class_v && (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 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 get_array() & noexcept; + simdjson_inline simdjson_result get_object() & noexcept; + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result get_value() noexcept; + + simdjson_inline simdjson_result is_null() noexcept; + template + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + template + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::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(*this).get(); + } + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_CONCEPTS + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { + static_assert(!sizeof(T), "The get 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 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, double, or bool."); + static_cast(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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " 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(out); // to get rid of unused errors + return UNINITIALIZED; +#endif // SIMDJSON_SUPPORTS_CONCEPTS + } + + /** @overload template error_code get(T &out) & noexcept */ + template simdjson_inline error_code get(T &out) && noexcept; + simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (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 + explicit simdjson_inline operator T() noexcept(false); + simdjson_inline operator array() & noexcept(false); + simdjson_inline operator 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 raw_json_string() noexcept(false); + simdjson_inline operator bool() noexcept(false); + simdjson_inline operator value() noexcept(false); +#endif + simdjson_inline simdjson_result count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) & noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result end() & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(const char *key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + + simdjson_inline simdjson_result type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + + simdjson_inline simdjson_result current_location() noexcept; + simdjson_inline int32_t current_depth() const noexcept; + simdjson_inline bool is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + simdjson_inline simdjson_result raw_json_token() noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + +private: + document *doc{nullptr}; +}; +} // namespace ondemand +} // namespace ppc64 +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public ppc64::implementation_simdjson_result_base { +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 get_array() & noexcept; + simdjson_inline simdjson_result get_object() & noexcept; + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result get_value() noexcept; + simdjson_inline simdjson_result is_null() noexcept; + + template simdjson_inline simdjson_result get() & noexcept; + template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept; + + template simdjson_inline error_code get(T &out) & noexcept; + template simdjson_inline error_code get(T &out) && noexcept; +#if SIMDJSON_EXCEPTIONS + + using ppc64::implementation_simdjson_result_base::operator*; + using ppc64::implementation_simdjson_result_base::operator->; + template ::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); + 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); + simdjson_inline operator ppc64::ondemand::value() noexcept(false); +#endif + simdjson_inline simdjson_result count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) & noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result end() & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(const char *key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + simdjson_inline simdjson_result type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + simdjson_inline simdjson_result current_location() noexcept; + simdjson_inline int32_t current_depth() const noexcept; + simdjson_inline bool at_end() const noexcept; + simdjson_inline bool is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + /** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */ + simdjson_inline simdjson_result raw_json_token() noexcept; + + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (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 : public ppc64::implementation_simdjson_result_base { +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 get_array() & noexcept; + simdjson_inline simdjson_result get_object() & noexcept; + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result get_value() noexcept; + simdjson_inline simdjson_result is_null() noexcept; + + template simdjson_inline simdjson_result get() & noexcept; + template simdjson_inline simdjson_result get() && noexcept; + + template simdjson_inline error_code get(T &out) & noexcept; + template simdjson_inline error_code get(T &out) && noexcept; +#if SIMDJSON_EXCEPTIONS + template + 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); + simdjson_inline operator ppc64::ondemand::value() noexcept(false); +#endif + simdjson_inline simdjson_result count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) & noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result end() & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(const char *key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + simdjson_inline simdjson_result type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + simdjson_inline simdjson_result current_location() noexcept; + simdjson_inline simdjson_result current_depth() const noexcept; + simdjson_inline simdjson_result is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + /** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */ + simdjson_inline simdjson_result raw_json_token() noexcept; + + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (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 +#include +#include +#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(); + * + */ + inline size_t truncated_bytes() const noexcept; + + class iterator { + public: + using value_type = simdjson_result; + using reference = simdjson_result; + 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. + * + * 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) + */ + simdjson_inline document_stream( + ondemand::parser &parser, + const uint8_t *buf, + size_t len, + size_t batch_size, + bool allow_comma_separated + ) 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. */ + inline void next_document() 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; + /** + * 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 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. + */ + 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; + friend struct simdjson::internal::simdjson_result_base; +}; // document_stream + +} // namespace ondemand +} // namespace ppc64 +} // namespace simdjson + +namespace simdjson { +template<> +struct simdjson_result : public ppc64::implementation_simdjson_result_base { +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 so you can use C++ algorithms that rely on pairs. + */ +class field : public std::pair { +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 unescaped_key(bool allow_replacement = false) 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. 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 + 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; + /** + * 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 start(value_iterator &parent_iter) noexcept; + static simdjson_inline simdjson_result start(const value_iterator &parent_iter, raw_json_string key) noexcept; + friend struct simdjson_result; + friend class object_iterator; +}; + +} // namespace ondemand +} // namespace ppc64 +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public ppc64::implementation_simdjson_result_base { +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 unescaped_key(bool allow_replacement = false) noexcept; + template + simdjson_inline error_code unescaped_key(string_type &receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result key() noexcept; + simdjson_inline simdjson_result key_raw_json_token() noexcept; + simdjson_inline simdjson_result escaped_key() noexcept; + simdjson_inline simdjson_result value() noexcept; +}; + +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ONDEMAND_FIELD_H +/* end file simdjson/generic/ondemand/field.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 */ +/* 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 { + +/** + * 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 + */ + simdjson_inline simdjson_result begin() noexcept; + simdjson_inline simdjson_result 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"); + * ``` + * 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 find_field(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result 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 find_field_unordered(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) && 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("/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 at_pointer(std::string_view json_pointer) noexcept; + + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + inline simdjson_result at_path(std::string_view json_path) noexcept; + + /** + * Get all values matching the given JSONPath expression with wildcard support. + * Supports wildcard patterns like ".*" to match all object fields. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern + */ + inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + + /** + * Reset the iterator so that we are pointing back at the + * beginning of the object. You should still consume values only once even if you + * 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 reset() & 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 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 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 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + 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 + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(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 + requires(std::is_class_v && (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 start(value_iterator &iter) noexcept; + static simdjson_inline simdjson_result start_root(value_iterator &iter) noexcept; + static simdjson_inline simdjson_result 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{}; + + friend class value; + friend class document; + friend struct simdjson_result; +}; + +} // namespace ondemand +} // namespace ppc64 +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public ppc64::implementation_simdjson_result_base { +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 begin() noexcept; + simdjson_inline simdjson_result end() noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) && noexcept; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + inline simdjson_result reset() noexcept; + inline simdjson_result is_empty() noexcept; + inline simdjson_result count_fields() & noexcept; + inline simdjson_result raw_json() noexcept; + #if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h + + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(out) ); + } + return SUCCESS; + } + +#if SIMDJSON_STATIC_REFLECTION + // TODO: move this code into object-inl.h + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) noexcept { + if (error()) { return error(); } + return first.extract_into(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; + + // + // Iterator interface + // + + // Reads key and value, yielding them to the user. + // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline simdjson_result 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}; +#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; + friend class object; +}; + +} // namespace ondemand +} // namespace ppc64 +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public ppc64::implementation_simdjson_result_base { +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 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 &) const noexcept; + // Assumes it's being compared with the end. true if depth >= iter->depth. + simdjson_inline bool operator!=(const simdjson_result &) const noexcept; + // Checks for ']' and ',' + simdjson_inline simdjson_result &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/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 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 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 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 to_json_string(ppc64::ondemand::array& x) noexcept; +inline simdjson_result to_json_string(simdjson_result x); +inline simdjson_result to_json_string(simdjson_result x); +inline simdjson_result to_json_string(simdjson_result x); +inline simdjson_result to_json_string(simdjson_result 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 + requires(!std::same_as && + !std::same_as && + !std::same_as && + !std::same_as) +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 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 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&& 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&& 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 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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +#include +#if SIMDJSON_STATIC_REFLECTION +#include +// #include // for std::define_static_string - header not available yet +#endif + +namespace simdjson { + +////////////////////////////// +// Number deserialization +////////////////////////////// + +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + uint64_t x; + SIMDJSON_TRY(val.get_uint64().get(x)); + if (x > (limits::max)()) { + return NUMBER_OUT_OF_RANGE; + } + out = static_cast(x); + return SUCCESS; +} + +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + double x; + SIMDJSON_TRY(val.get_double().get(x)); + out = static_cast(x); + return SUCCESS; +} + +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + 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(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 +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v) { + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + out = T{str}; + return SUCCESS; +} + + +/** + * 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>(). + */ +template +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { + using value_type = typename std::remove_cvref_t::value_type; + static_assert( + deserializable, + "The specified type inside the container must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the container must default constructible."); + ppc64::ondemand::array arr; + if constexpr (std::is_same_v, ppc64::ondemand::array>) { + arr = val; + } else { + SIMDJSON_TRY(val.get_array().get(arr)); + } + + for (auto v : arr) { + if constexpr (concepts::returns_reference) { + if (auto const err = v.get().get(concepts::emplace_one(out)); + err) { + // If an error occurs, the empty element that we just inserted gets + // removed. We're not using a temp variable because if T is a heavy + // type, we want the valid path to be the fast path and the slow path be + // the path that has errors in it. + if constexpr (requires { out.pop_back(); }) { + static_cast(out.pop_back()); + } + return err; + } + } else { + value_type temp; + if (auto const err = v.get().get(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 +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { + using value_type = typename std::remove_cvref_t::mapped_type; + static_assert( + deserializable, + "The specified value type inside the container must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "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().get(this_value)); + [[maybe_unused]] std::pair 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 +error_code tag_invoke(deserialize_tag, ppc64::ondemand::object &obj, T &out) noexcept { + using value_type = typename std::remove_cvref_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 +error_code tag_invoke(deserialize_tag, ppc64::ondemand::value &val, T &out) noexcept { + ppc64::ondemand::object obj; + SIMDJSON_TRY(val.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} + +template +error_code tag_invoke(deserialize_tag, ppc64::ondemand::document &doc, T &out) noexcept { + ppc64::ondemand::object obj; + SIMDJSON_TRY(doc.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} + +template +error_code tag_invoke(deserialize_tag, ppc64::ondemand::document_reference &doc, T &out) noexcept { + 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. + * + * If constructing T is nothrow, this conversion should be nothrow as well since + * we return MEMALLOC if we're not able to allocate memory instead of throwing + * the error message. + * + * @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 +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::element_type, ValT>) { + using element_type = typename std::remove_cvref_t::element_type; + + // For better error messages, don't use these as constraints on + // the tag_invoke CPO. + static_assert( + deserializable, + "The specified type inside the unique_ptr must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the unique_ptr must default constructible."); + + auto ptr = new (std::nothrow) element_type(); + if (ptr == nullptr) { + return MEMALLOC; + } + SIMDJSON_TRY(val.template get(*ptr)); + out.reset(ptr); + return SUCCESS; +} + +/** + * This CPO (Customization Point Object) will help deserialize into optional types. + */ +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable::value_type, decltype(val)>) { + using value_type = typename std::remove_cvref_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(out.value())); + return SUCCESS; +} + + +#if SIMDJSON_STATIC_REFLECTION + + +template +constexpr bool user_defined_type = (std::is_class_v +&& !std::is_same_v && !std::is_same_v && !concepts::optional_type && +!concepts::appendable_containers); + + +template + requires(user_defined_type && std::is_class_v) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { + ppc64::ondemand::object obj; + if constexpr (std::is_same_v, ppc64::ondemand::object>) { + obj = val; + } else { + SIMDJSON_TRY(val.get_object().get(obj)); + } + 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)); + if constexpr (concepts::optional_type) { + // for optional members, it's ok if the key is missing + auto error = obj[key].get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + if(error == NO_SUCH_FIELD) { + out.[:mem:].reset(); + continue; + } + return error; + } + } else { + // for non-optional members, the key must be present + SIMDJSON_TRY(obj[key].get(out.[:mem:])); + } + } + }; + return simdjson::SUCCESS; +} + +// Support for enum deserialization - deserialize from string representation using expand approach from P2996R12 +template + requires(std::is_enum_v) +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)); + constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T)); + template for (constexpr auto enum_val : enumerators) { + if (str == std::meta::identifier_of(enum_val)) { + out = [:enum_val:]; + return SUCCESS; + } + }; + + return INCORRECT_TYPE; +#else + // Fallback: deserialize as integer if reflection not available + std::underlying_type_t int_val; + SIMDJSON_TRY(val.get(int_val)); + out = static_cast(int_val); + return SUCCESS; +#endif +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr &out) noexcept { + if (!out) { + out = std::make_unique(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr &out) noexcept { + if (!out) { + out = std::make_shared(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +#endif // SIMDJSON_STATIC_REFLECTION + +//////////////////////////////////////// +// Unique pointers +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + 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 &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + 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 &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_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 &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_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 &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(); + } + int64_t temp; + SIMDJSON_TRY(val.get_int64().get(temp)); + *out = static_cast(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::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::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::started(value_iterator &iter) noexcept { + bool has_value; + SIMDJSON_TRY(iter.started_array().get(has_value)); + return array(iter); +} + +simdjson_inline simdjson_result array::begin() noexcept { +#if SIMDJSON_DEVELOPMENT_CHECKS + if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } +#endif + return array_iterator(iter); +} +simdjson_inline simdjson_result 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 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(starting_point), size_t(final_point - starting_point)); +} + +SIMDJSON_PUSH_DISABLE_WARNINGS +SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING +simdjson_inline simdjson_result array::count_elements() & 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 array after counting the number of elements. + iter.reset_array(); + return count; +} +SIMDJSON_POP_DISABLE_WARNINGS + +simdjson_inline simdjson_result array::is_empty() & noexcept { + bool is_not_empty; + auto error = iter.reset_array().get(is_not_empty); + if(error) { return error; } + return !is_not_empty; +} + +inline simdjson_result array::reset() & noexcept { + return iter.reset_array(); +} + +inline simdjson_result array::at_pointer(std::string_view json_pointer) noexcept { + if (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; } + + // Read the array index + size_t array_index = 0; + size_t i; + for (i = 0; i < json_pointer.length() && json_pointer[i] != '/'; i++) { + uint8_t digit = uint8_t(json_pointer[i] - '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; } + array_index = array_index*10 + digit; + } + + // 0 followed by other digits is invalid + if (i > 1 && json_pointer[0] == '0') { return INVALID_JSON_POINTER; } // "JSON pointer array index has other characters after 0" + + // Empty string is invalid; so is a "/" with no digits before it + if (i == 0) { return INVALID_JSON_POINTER; } // "Empty string in JSON pointer array index" + // 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 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); +} + +inline simdjson_result> array::at_path_with_wildcard(std::string_view json_path) noexcept { + std::vector result; + + 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; + // Wildcard case + if(key=="*"){ + for(auto element: *this){ + + if(element.error()){ + return element.error(); + } + + if(remaining_path.empty()){ + // Use value_unsafe() because we've already checked for errors above. + // The 'element' is a simdjson_result wrapper, and we need to extract + // the underlying value. value_unsafe() is safe here because error() returned false. + result.push_back(std::move(element).value_unsafe()); + + }else{ + auto nested_result = element.at_path_with_wildcard(remaining_path); + + if(nested_result.error()){ + return nested_result.error(); + } + // Same logic as above. + std::vector nested_matches = std::move(nested_result).value_unsafe(); + + result.insert(result.end(), + std::make_move_iterator(nested_matches.begin()), + std::make_move_iterator(nested_matches.end())); + } + } + return result; + }else{ + // Specific index case in which we access the element at the given index + size_t idx=0; + + for(char c:key){ + if(c < '0' || c > '9'){ + return INVALID_JSON_POINTER; + } + idx = idx*10 + (c - '0'); + } + + auto element = at(idx); + + if(element.error()){ + return element.error(); + } + + if(remaining_path.empty()){ + result.push_back(std::move(element).value_unsafe()); + return result; + }else{ + return element.at_path_with_wildcard(remaining_path); + } + } +} + +simdjson_inline simdjson_result array::at(size_t index) noexcept { + 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::simdjson_result( + ppc64::ondemand::array &&value +) noexcept + : implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept + : implementation_simdjson_result_base(error) +{ +} + +simdjson_inline simdjson_result simdjson_result::begin() noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() noexcept { + if (error()) { return error(); } + return first.end(); +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::is_empty() & noexcept { + if (error()) { return error(); } + return first.is_empty(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path(json_path); +} +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path_with_wildcard(json_path); +} +simdjson_inline simdjson_result simdjson_result::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} +{} + +simdjson_inline simdjson_result 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::simdjson_result( + ppc64::ondemand::array_iterator &&value +) noexcept + : ppc64::implementation_simdjson_result_base(std::forward(value)) +{ + first.iter.assert_is_valid(); +} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : ppc64::implementation_simdjson_result_base({}, error) +{ +} + +simdjson_inline simdjson_result simdjson_result::operator*() noexcept { + if (error()) { return error(); } + return *first; +} +simdjson_inline bool simdjson_result::operator==(const simdjson_result &other) const noexcept { + if (!first.iter.is_valid()) { return !error(); } + return first == other.first; +} +simdjson_inline bool simdjson_result::operator!=(const simdjson_result &other) const noexcept { + if (!first.iter.is_valid()) { return error(); } + return first != other.first; +} +simdjson_inline simdjson_result &simdjson_result::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::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 */ + +namespace simdjson { +namespace ppc64 { +namespace ondemand { + +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 value::get_array() noexcept { + return array::start(iter); +} +simdjson_inline simdjson_result value::get_object() noexcept { + return object::start(iter); +} +simdjson_inline simdjson_result value::start_or_resume_object() noexcept { + if (iter.at_start()) { + return get_object(); + } else { + return object::resume(iter); + } +} + +simdjson_inline simdjson_result value::get_raw_json_string() noexcept { + return iter.get_raw_json_string(); +} +simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { + return iter.get_string(allow_replacement); +} +template +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 value::get_wobbly_string() noexcept { + return iter.get_wobbly_string(); +} +simdjson_inline simdjson_result value::get_double() noexcept { + return iter.get_double(); +} +simdjson_inline simdjson_result value::get_double_in_string() noexcept { + return iter.get_double_in_string(); +} +simdjson_inline simdjson_result value::get_uint64() noexcept { + return iter.get_uint64(); +} +simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { + return iter.get_uint64_in_string(); +} +simdjson_inline simdjson_result value::get_int64() noexcept { + return iter.get_int64(); +} +simdjson_inline simdjson_result value::get_int64_in_string() noexcept { + return iter.get_int64_in_string(); +} +simdjson_inline simdjson_result value::get_bool() noexcept { + return iter.get_bool(); +} +simdjson_inline simdjson_result value::is_null() noexcept { + return iter.is_null(); +} + +template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result 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); } +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(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(bool& out) noexcept { return get_bool().get(out); } + +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline value::operator T() noexcept(false) { + return get(); +} +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 value::begin() & noexcept { + return get_array().begin(); +} +simdjson_inline simdjson_result value::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result value::count_elements() & noexcept { + simdjson_result 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 value::count_fields() & noexcept { + simdjson_result answer; + auto a = get_object(); + answer = a.count_fields(); + iter.move_at_start(); + return answer; +} +simdjson_inline simdjson_result value::at(size_t index) noexcept { + auto a = get_array(); + return a.at(index); +} + +simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result value::find_field(const char *key) noexcept { + return start_or_resume_object().find_field(key); +} + +simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result value::find_field_unordered(const char *key) noexcept { + return start_or_resume_object().find_field_unordered(key); +} + +simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { + return start_or_resume_object()[key]; +} +simdjson_inline simdjson_result value::operator[](const char *key) noexcept { + return start_or_resume_object()[key]; +} + +simdjson_inline simdjson_result value::type() noexcept { + return iter.type(); +} + +simdjson_inline simdjson_result 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 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 value::is_integer() noexcept { + return iter.is_integer(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { + return iter.get_number_type(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { + return iter.get_number(); +} + +simdjson_inline std::string_view value::raw_json_token() noexcept { + return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); +} + +simdjson_inline simdjson_result 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 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::at_pointer(std::string_view json_pointer) noexcept { + 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::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; + } +} + +inline simdjson_result> value::at_path_with_wildcard(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_with_wildcard(json_path); + case json_type::object: + return (*this).get_object().at_path_with_wildcard(json_path); + default: + return INVALID_JSON_POINTER; + } +} + +} // namespace ondemand +} // namespace ppc64 +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + ppc64::ondemand::value &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base(error) +{ +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + if (error()) { return error(); } + return {}; +} + +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) noexcept { + if (error()) { return error(); } + return first.find_field(key); +} + +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} + +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) noexcept { + if (error()) { return error(); } + return first[key]; +} + +simdjson_inline simdjson_result simdjson_result::get_array() noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} + +template<> simdjson_inline error_code simdjson_result::get(ppc64::ondemand::value &out) noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} + +template simdjson_inline simdjson_result simdjson_result::get() noexcept { + if (error()) { return error(); } + return first.get(); +} +template simdjson_inline error_code simdjson_result::get(T &out) noexcept { + if (error()) { return error(); } + return first.get(out); +} + +template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { + if (error()) { return error(); } + return std::move(first); +} + +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline simdjson_result::operator T() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first.get(); +} +simdjson_inline simdjson_result::operator ppc64::ondemand::array() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator ppc64::ondemand::object() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator ppc64::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { + if (error()) { return error(); } + return first.raw_json(); +} + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { + if (error()) { return error(); } + return first.current_depth(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer( + std::string_view json_pointer) noexcept { + if (error()) { + return error(); + } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + +inline simdjson_result> simdjson_result::at_path_with_wildcard( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path_with_wildcard(json_path); +} + +} // 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 */ + +namespace simdjson { +namespace ppc64 { +namespace ondemand { + +simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept + : iter{std::forward(_iter)} +{ + logger::log_start_value(iter, "document"); +} + +simdjson_inline document document::start(json_iterator &&iter) noexcept { + return document(std::forward(iter)); +} + +inline void document::rewind() noexcept { + iter.rewind(); +} + +inline std::string document::to_debug_string() noexcept { + return iter.to_string(); +} + +inline simdjson_result 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 document::start_or_resume_object() noexcept { + if (iter.at_root()) { + return get_object(); + } else { + return object::resume(resume_value_iterator()); + } +} +simdjson_inline simdjson_result document::get_value() noexcept { + // Make sure we start any arrays or objects before returning, so that start_root_() + // 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 document::get_array() & noexcept { + auto value = get_root_value_iterator(); + return array::start_root(value); +} +simdjson_inline simdjson_result 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 document::get_uint64() noexcept { + return get_root_value_iterator().get_root_uint64(true); +} +simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { + return get_root_value_iterator().get_root_uint64_in_string(true); +} +simdjson_inline simdjson_result document::get_int64() noexcept { + return get_root_value_iterator().get_root_int64(true); +} +simdjson_inline simdjson_result document::get_int64_in_string() noexcept { + return get_root_value_iterator().get_root_int64_in_string(true); +} +simdjson_inline simdjson_result document::get_double() noexcept { + return get_root_value_iterator().get_root_double(true); +} +simdjson_inline simdjson_result document::get_double_in_string() noexcept { + return get_root_value_iterator().get_root_double_in_string(true); +} +simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { + return get_root_value_iterator().get_root_string(true, allow_replacement); +} +template +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 document::get_wobbly_string() noexcept { + return get_root_value_iterator().get_root_wobbly_string(true); +} +simdjson_inline simdjson_result document::get_raw_json_string() noexcept { + return get_root_value_iterator().get_root_raw_json_string(true); +} +simdjson_inline simdjson_result document::get_bool() noexcept { + return get_root_value_iterator().get_root_bool(true); +} +simdjson_inline simdjson_result document::is_null() noexcept { + return get_root_value_iterator().is_root_null(true); +} + +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result 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); } +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(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(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 document::get() && noexcept { return get_raw_json_string(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_value(); } + +#if SIMDJSON_EXCEPTIONS +template +simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } +template +simdjson_inline document::operator T() & noexcept(false) { return get(); } +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 document::count_elements() & noexcept { + auto a = get_array(); + simdjson_result 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 document::count_fields() & noexcept { + auto a = get_object(); + simdjson_result 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 document::at(size_t index) & noexcept { + auto a = get_array(); + return a.at(index); +} +simdjson_inline simdjson_result document::begin() & noexcept { + return get_array().begin(); +} +simdjson_inline simdjson_result document::end() & noexcept { + return {}; +} + +simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field(const char *key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::find_field_unordered(const char *key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { + return start_or_resume_object()[key]; +} +simdjson_inline simdjson_result 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 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(starting_point), size_t(final_point - starting_point)); +} + +simdjson_inline simdjson_result document::type() noexcept { + return get_root_value_iterator().type(); +} + +simdjson_inline simdjson_result 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 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 document::is_integer() noexcept { + return get_root_value_iterator().is_root_integer(true); +} + +simdjson_inline simdjson_result document::get_number_type() noexcept { + return get_root_value_iterator().get_root_number_type(true); +} + +simdjson_inline simdjson_result document::get_number() noexcept { + return get_root_value_iterator().get_root_number(true); +} + + +simdjson_inline simdjson_result document::raw_json_token() noexcept { + auto _iter = get_root_value_iterator(); + return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); +} + +simdjson_inline simdjson_result 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 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; + } +} + +simdjson_inline simdjson_result> document::at_path_with_wildcard(std::string_view json_path) noexcept { + rewind(); // Rewind the document each time 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().at_path_with_wildcard(json_path); + case json_type::object: + return (*this).get_object().at_path_with_wildcard(json_path); + default: + return INVALID_JSON_POINTER; + } +} + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (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) { + // 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::simdjson_result( + ppc64::ondemand::document &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base( + error + ) +{ +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { + if (error()) { return error(); } + return first.get_value(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} + +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} + +template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; +template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first); +} +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(ppc64::ondemand::document &out) & noexcept = delete; +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(ppc64::ondemand::document &out) && noexcept { + if (error()) { return error(); } + out = std::forward(first); + return SUCCESS; +} + +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} + +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} + +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} + +simdjson_inline bool simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} + +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} + +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} + +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} + + +#if SIMDJSON_EXCEPTIONS +template ::value == false>::type> +simdjson_inline simdjson_result::operator T() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator ppc64::ondemand::array() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator ppc64::ondemand::object() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator ppc64::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator ppc64::ondemand::value() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline bool simdjson_result::at_end() const noexcept { + if (error()) { return error(); } + return first.at_end(); +} + + +simdjson_inline int32_t simdjson_result::current_depth() const noexcept { + if (error()) { return error(); } + return first.current_depth(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path(json_path); +} + +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path_with_wildcard(json_path); +} + +#if SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(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 document_reference::get_array() & noexcept { return doc->get_array(); } +simdjson_inline simdjson_result 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 document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } +simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } +simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } +template +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 document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } +simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } +simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } +simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } +simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline document_reference::operator T() noexcept(false) { return get(); } +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 document_reference::count_elements() & noexcept { return doc->count_elements(); } +simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } +simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } +simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } +simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } +simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } +simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } +simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } +simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } +simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } +simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } +simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } +simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } +simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } +simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } +simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } +simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } +simdjson_inline simdjson_result> document_reference::at_path_with_wildcard(std::string_view json_path) noexcept { return doc->at_path_with_wildcard(json_path); } +simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} +simdjson_inline document_reference::operator document&() const noexcept { return *doc; } +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code document_reference::extract_into(T& out) & noexcept { + return doc->extract_into(out); +} +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION +} // namespace ondemand +} // namespace ppc64 +} // namespace simdjson + + + +namespace simdjson { +simdjson_inline simdjson_result::simdjson_result(ppc64::ondemand::document_reference value, error_code error) + noexcept : implementation_simdjson_result_base(std::forward(value), error) {} + + +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { + if (error()) { return error(); } + return first.get_value(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} +template <> +simdjson_warn_unused simdjson_inline error_code simdjson_result::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::get(ppc64::ondemand::document_reference &out) && noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline simdjson_result::operator T() noexcept(false) { + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + if (error()) { throw simdjson_error(error()); } + return first.get(); +} +simdjson_inline simdjson_result::operator ppc64::ondemand::array() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator ppc64::ondemand::object() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator ppc64::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator ppc64::ondemand::value() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path_with_wildcard(json_path); +} +#if SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(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 +#include + +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 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 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 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 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 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 +) 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}, + 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}, + allow_comma_separated{false}, + 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 { + 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 document_stream::iterator::operator*() noexcept { + return simdjson_result(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; } + doc_index = batch_start; + 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 + 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(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; + } + } +} + +inline void document_stream::next_document() noexcept { + // 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(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; + if (remaining <= batch_size) { + return p.implementation->stage1(&buf[_batch_start], remaining, stage1_mode::streaming_final); + } else { + return p.implementation->stage1(&buf[_batch_start], batch_size, stage1_mode::streaming_partial); + } +} + +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 { + auto depth = stream->doc.iter.depth(); + auto cur_struct_index = stream->doc.iter._root - stream->parser->implementation->structural_indexes.get(); + + // If at root, process the first token to determine if scalar value + if (stream->doc.iter.at_root()) { + 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 + // TODO: We could remove trailing whitespaces + // This returns a string spanning from start of value to the beginning of the next document (excluded) + { + auto next_index = 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(stream->buf) + current_index(); + while(svlen > 1 && (std::isspace(start[svlen-1]) || start[svlen-1] == '\0')) { + svlen--; + } + return std::string_view(start, svlen); + } + } + cur_struct_index++; + } + + while (cur_struct_index <= static_cast(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(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::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base(error) +{ +} +simdjson_inline simdjson_result::simdjson_result( + ppc64::ondemand::document_stream &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(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() {} + +simdjson_inline field::field(raw_json_string key, ondemand::value &&value) noexcept + : std::pair(key, std::forward(value)) +{ +} + +simdjson_inline simdjson_result 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::start(const value_iterator &parent_iter, raw_json_string key) noexcept { + return field(key, parent_iter.child()); +} + +simdjson_inline simdjson_warn_unused simdjson_result 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 answer = first.unescape(second.iter.json_iter(), allow_replacement); + first.consume(); + return answer; +} + +template +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) ); + 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(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(first.buf), end_quote - first.buf); +} + +simdjson_inline value &field::value() & noexcept { + return second; +} + +simdjson_inline value field::value() && noexcept { + return std::forward(*this).second; +} + +} // namespace ondemand +} // namespace ppc64 +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + ppc64::ondemand::field &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base(error) +{ +} + +simdjson_inline simdjson_result simdjson_result::key() noexcept { + if (error()) { return error(); } + return first.key(); +} + +simdjson_inline simdjson_result simdjson_result::key_raw_json_token() noexcept { + if (error()) { return error(); } + return first.key_raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::escaped_key() noexcept { + if (error()) { return error(); } + return first.escaped_key(); +} + +simdjson_inline simdjson_result simdjson_result::unescaped_key(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.unescaped_key(allow_replacement); +} + +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::unescaped_key(string_type &receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.unescaped_key(receiver, allow_replacement); +} + +simdjson_inline simdjson_result simdjson_result::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(other.token)), + parser{other.parser}, + _string_buf_loc{other._string_buf_loc}, + error{other.error}, + _depth{other._depth}, + _root{other._root}, + _streaming{other._streaming} +{ + 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; + 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} + +{ + 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 '"': + if(*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(TAPE_ERROR, "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; +} + +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(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 json_iterator::current_location() const noexcept { + if (!is_alive()) { // Unrecoverable error + if (!at_root()) { + return reinterpret_cast(token.peek(-1)); + } else { + return reinterpret_cast(token.peek()); + } + } + if (at_end()) { + return OUT_OF_BOUNDS; + } + return reinterpret_cast(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 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 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::simdjson_result(ppc64::ondemand::json_iterator &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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; + default: SIMDJSON_UNREACHABLE(); + } + return out; +} + +#if SIMDJSON_EXCEPTIONS +inline std::ostream& operator<<(std::ostream& out, simdjson_result &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::simdjson_result(ppc64::ondemand::json_type &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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 +#include + +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 +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_s); + if (size <= 0) return std::string(); + std::unique_ptr 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 +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)...); +} + +template +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)...); + 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): #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 */ +/* amalgamation skipped (editor-only): #endif */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace ppc64 { +namespace ondemand { + +simdjson_inline simdjson_result 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} +simdjson_inline simdjson_result 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} +simdjson_inline simdjson_result object::operator[](const std::string_view key) & noexcept { + return find_field_unordered(key); +} +simdjson_inline simdjson_result object::operator[](const std::string_view key) && noexcept { + return std::forward(*this).find_field_unordered(key); +} +simdjson_inline simdjson_result object::find_field(const std::string_view key) & noexcept { + 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} +simdjson_inline simdjson_result object::find_field(const std::string_view key) && noexcept { + 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} + +simdjson_inline simdjson_result object::start(value_iterator &iter) noexcept { + SIMDJSON_TRY( iter.start_object().error() ); + return object(iter); +} +simdjson_inline simdjson_result 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 object::raw_json() noexcept { + 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(starting_point), size_t(final_point - starting_point)); +} + +simdjson_inline simdjson_result 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::begin() noexcept { +#if SIMDJSON_DEVELOPMENT_CHECKS + if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } +#endif + return object_iterator(iter); +} +simdjson_inline simdjson_result object::end() noexcept { + return object_iterator(iter); +} + +inline simdjson_result object::at_pointer(std::string_view json_pointer) noexcept { + if (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 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 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); +} + +inline simdjson_result> object::at_path_with_wildcard(std::string_view json_path) noexcept { + std::vector result; + + 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; + // 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()) { + result.push_back(std::move(val)); + } else { + auto nested_result = val.at_path_with_wildcard(remaining_path); + + if (nested_result.error()) { + return nested_result.error(); + } + // Extract and append all nested matches to our result + std::vector nested_vec; + SIMDJSON_TRY(std::move(nested_result).get(nested_vec)); + + result.insert(result.end(), + std::make_move_iterator(nested_vec.begin()), + std::make_move_iterator(nested_vec.end())); + } + } + return result; + } else { + value val; + SIMDJSON_TRY(find_field(key).get(val)); + + if (remaining_path.empty()) { + result.push_back(std::move(val)); + return result; + } else { + return val.at_path_with_wildcard(remaining_path); + } + } +} + +simdjson_inline simdjson_result 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 object::is_empty() & noexcept { + 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 object::reset() & noexcept { + return iter.reset_object(); +} + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (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) { + // 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::simdjson_result(ppc64::ondemand::object &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(error) {} + +simdjson_inline simdjson_result simdjson_result::begin() noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() noexcept { + if (error()) { return error(); } + return first.end(); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) && noexcept { + if (error()) { return error(); } + return std::forward(first).find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) && noexcept { + if (error()) { return error(); } + return std::forward(first)[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) && noexcept { + if (error()) { return error(); } + return std::forward(first).find_field(key); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path_with_wildcard(json_path); +} + +inline simdjson_result simdjson_result::reset() noexcept { + if (error()) { return error(); } + return first.reset(); +} + +inline simdjson_result simdjson_result::is_empty() noexcept { + if (error()) { return error(); } + return first.is_empty(); +} + +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} + +simdjson_inline simdjson_result simdjson_result::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} +{} + +simdjson_inline simdjson_result 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::simdjson_result( + ppc64::ondemand::object_iterator &&value +) noexcept + : implementation_simdjson_result_base(std::forward(value)) +{ + first.iter.assert_is_valid(); +} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base({}, error) +{ +} + +simdjson_inline simdjson_result simdjson_result::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::operator==(const simdjson_result &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::operator!=(const simdjson_result &other) const noexcept { + if (!first.iter.is_valid()) { return error(); } + return first != other.first; +} +// Checks for ']' and ',' +simdjson_inline simdjson_result &simdjson_result::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/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; + 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 parser::iterate(padded_string_view json) & noexcept { + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } + + json.remove_utf8_bom(); + + // 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(json.data()), json.length(), stage1_mode::regular) ); + return document::start({ reinterpret_cast(json.data()), this }); +} + +#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept { + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } + + json.remove_utf8_bom(); + + // 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(json.data()), json.length(), stage1_mode::regular); + if (err) { + if (err != UNCLOSED_STRING) + return err; + } + return document::start({ reinterpret_cast(json.data()), this, true }); +} +#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON + +simdjson_warn_unused simdjson_inline simdjson_result 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 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 parser::iterate(std::string_view json, size_t allocated) & noexcept { + return iterate(padded_string_view(json, allocated)); +} + +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(std::string &json) & noexcept { + return iterate(pad_with_reserve(json)); +} + +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const std::string &json) & noexcept { + return iterate(padded_string_view(json)); +} + +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const simdjson_result &result) & noexcept { + // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 parser::iterate(const simdjson_result &result) & noexcept { + // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 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(json.data()), json.length(), stage1_mode::regular) ); + return json_iterator(reinterpret_cast(json.data()), this); +} + +inline simdjson_result 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; + } + if(allow_comma_separated && batch_size < len) { batch_size = len; } + return document_stream(*this, buf, len, batch_size, allow_comma_separated); +} + +inline simdjson_result 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(buf), len, batch_size, allow_comma_separated); +} +inline simdjson_result 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 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 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 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_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 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(dst), end-dst); + dst = end; + return result; +} + +simdjson_inline simdjson_warn_unused simdjson_result 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(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 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& parser::get_parser_instance() { + std::unique_ptr& 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& 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 parser_instance = nullptr; + return parser_instance; +} + + +} // namespace ondemand +} // namespace ppc64 +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result(ppc64::ondemand::parser &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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(buf); +} + +simdjson_inline char raw_json_string::operator[](size_t i) const noexcept { + return reinterpret_cast(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()); + } + const char * r{raw()}; + size_t pos{0}; + for(;pos < target.size();pos++) { + if(r[pos] != target[pos]) { return false; } + } + if(r[pos] != '"') { return false; } + return true; +} + +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 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 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::simdjson_result(ppc64::ondemand::raw_json_string &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(error) {} + +simdjson_inline simdjson_result simdjson_result::raw() const noexcept { + if (error()) { return error(); } + return first.raw(); +} +simdjson_inline char simdjson_result::operator[](size_t i) const noexcept { + if (error()) { return error(); } + return first[i]; +} +simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::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 simdjson_result::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::simdjson_result(ppc64::ondemand::token_iterator &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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 { + +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 value_iterator::start_object() noexcept { + SIMDJSON_TRY( start_container('{', "Not an object", "object") ); + return started_object(); +} + +simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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 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 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_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 value_iterator::start_array() noexcept { + SIMDJSON_TRY( start_container('[', "Not an array", "array") ); + return started_array(); +} + +simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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(!not_true); +} +simdjson_warn_unused simdjson_inline simdjson_result 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 value_iterator::get_string(bool allow_replacement) noexcept { + return get_raw_json_string().unescape(json_iter(), allow_replacement); +} +template +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; } + 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 value_iterator::get_wobbly_string() noexcept { + return get_raw_json_string().unescape_wobbly(json_iter()); +} +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_raw_json_string() noexcept { + auto json = peek_scalar("string"); + if (*json != '"') { return incorrect_type_error("Not a string"); } + advance_scalar("string"); + return raw_json_string(json+1); +} +simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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 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 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 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 value_iterator::is_integer() noexcept { + return numberparsing::is_integer(peek_non_root_scalar("integer")); +} +simdjson_inline simdjson_result value_iterator::get_number_type() noexcept { + return numberparsing::get_number_type(peek_non_root_scalar("integer")); +} +simdjson_inline simdjson_result 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 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 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.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 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.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 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 +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; } + 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 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 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; } + advance_scalar("string"); + return raw_json_string(json+1); +} +simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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.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 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.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 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 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 simdjson_result value_iterator::reset_array() noexcept { + if(error()) { return error(); } + move_at_container_start(); + return started_array(); +} + +simdjson_inline simdjson_result 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 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::simdjson_result(ppc64::ondemand::value_iterator &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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 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 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 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 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 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 to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result 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 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 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&& x) { + if (x.error()) { throw simdjson::simdjson_error(x.error()); } + return (out << x.value()); +} +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& 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 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 emails; }; + * + * std::string email; + * path_accessor::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 +#include +#include + +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 +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 +consteval auto make_field_step(const char (&name)[N]) { + return path_step(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 +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 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 +struct path_accessor { + using value = ::simdjson::ppc64::ondemand::value; + + static constexpr auto parser = json_path_parser(); + 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 + static inline simdjson_result access(DocOrValue& doc_or_val) noexcept { + // Validate path at compile time if T is a struct + if constexpr (std::is_class_v) { + 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(doc_or_val.get_value()); + } + + // Extract value at path directly into target with compile-time type validation + // Example: std::string name; path_accessor::extract_field(doc, name); + template + static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept { + static_assert(std::is_class_v, "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(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 + static consteval std::enable_if_t, 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 + static inline error_code extract_with_reflection(simdjson_result 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(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(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_value, target_ref); + } + } + } + // Skip unexpected characters and continue + else { + return extract_with_reflection(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 + static inline simdjson_result access_impl(simdjson_result 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_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_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_value); + } + } else { + return access_impl(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(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 + 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 + 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 + static consteval bool is_container_type() { + using BaseType = std::remove_cvref_t; + if constexpr (requires { typename BaseType::value_type; }) { + return true; + } + if constexpr (std::is_array_v) { + return true; + } + return false; + } + + // Extract element type from non-reflected container + template + using extract_element_type = std::conditional_t< + requires { typename std::remove_cvref_t::value_type; }, + typename std::remove_cvref_t::value_type, + std::conditional_t< + std::is_array_v>, + std::remove_extent_t>, + void + > + >; + + // Validate path matches struct definition + static consteval bool validate_path() { + if constexpr (!std::is_class_v) { + 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 +inline simdjson_result<::simdjson::ppc64::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = path_accessor; + return accessor::access(doc_or_val); +} + +// Overload without type parameter (no validation) +template +inline simdjson_result<::simdjson::ppc64::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = path_accessor; + return accessor::access(doc_or_val); +} + +// ============================================================================ +// JSON Pointer Compile-Time Support (RFC 6901) +// ============================================================================ + +// JSON Pointer parser: /field/0/nested (slash-separated) +template +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 +struct pointer_accessor { + using parser = json_pointer_parser; + 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) { + 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::is_array_like_reflected(current_type)) { + return false; + } + current_type = path_accessor::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 + static inline simdjson_result access_impl(simdjson_result 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(next_value); + } else { + auto obj = current.get_object().value_unsafe(); + auto next_value = obj.find_field_unordered(token); + return access_impl(next_value); + } + } + } + + // Access JSON value at pointer + template + static inline simdjson_result access(DocOrValue& doc_or_val) noexcept { + if constexpr (std::is_class_v) { + 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 current = doc_or_val.get_value(); + return access_impl<0>(current); + } + + // Extract value at pointer directly into target with type validation + template + static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept { + static_assert(std::is_class_v, "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 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 + static consteval std::enable_if_t, 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::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 +inline simdjson_result<::simdjson::ppc64::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = pointer_accessor; + return accessor::access(doc_or_val); +} + +// Overload without type parameter (no validation) +template +inline simdjson_result<::simdjson::ppc64::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = pointer_accessor; + 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 struct simd8; +template 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 +#include // visual studio or clang +#else +#include // 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 // for _mm_alignr_epi8 +#include // 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 + +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { +#if SIMDJSON_REGULAR_VISUAL_STUDIO + return _addcarry_u64(0, value1, value2, + reinterpret_cast(result)); +#else + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); +#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 struct simd8; +template 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 +#include // visual studio or clang +#else +#include // 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 // for _mm_alignr_epi8 +#include // 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(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 + 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(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; + + template> + struct base8: base> { + typedef uint16_t bitmask_t; + typedef uint32_t bitmask2_t; + + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m128i _value) : base>(_value) {} + + friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm_cmpeq_epi8(lhs, rhs); } + + static const int SIZE = sizeof(base>::value); + + template + simdjson_inline simd8 prev(const simd8 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: base8 { + static simdjson_inline simd8 splat(bool _value) { return _mm_set1_epi8(uint8_t(-(!!_value))); } + + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m128i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(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 operator~() const { return *this ^ true; } + }; + + template + struct base8_numeric: base8 { + static simdjson_inline simd8 splat(T _value) { return _mm_set1_epi8(_value); } + static simdjson_inline simd8 zero() { return _mm_setzero_si128(); } + static simdjson_inline simd8 load(const T values[16]) { + return _mm_loadu_si128(reinterpret_cast(values)); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } + + simdjson_inline base8_numeric() : base8() {} + simdjson_inline base8_numeric(const __m128i _value) : base8(_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 operator~() const { return *this ^ 0xFFu; } + + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { return _mm_add_epi8(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return _mm_sub_epi8(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } + + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 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 compress(uint32_t mask) would be + // sensible, but the AVX ISA makes this kind of approach difficult. + template + 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(pshufb_combine_table + pop1 * 8)); + __m128i answer = _mm_shuffle_epi8(pruned, compactmask); + _mm_storeu_si128(reinterpret_cast<__m128i *>(output), answer); + } + + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; + + // Signed bytes + template<> + struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } + + // Order-sensitive comparisons + simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epi8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epi8(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return _mm_cmpgt_epi8(*this, other); } + simdjson_inline simd8 operator<(const simd8 other) const { return _mm_cmpgt_epi8(other, *this); } + }; + + // Unsigned bytes + template<> + struct simd8: base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } + + // Saturated math + simdjson_inline simd8 saturating_add(const simd8 other) const { return _mm_adds_epu8(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm_subs_epu8(*this, other); } + + // Order-specific operations + simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epu8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epu8(*this, other); } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } + simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } + simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } + simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + simdjson_inline simd8 operator<(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } + simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } + simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } + simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return _mm_testz_si128(*this, bits); } + simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } + template + simdjson_inline simd8 shr() const { return simd8(_mm_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } + template + simdjson_inline simd8 shl() const { return simd8(_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 + simdjson_inline int get_bit() const { return _mm_movemask_epi8(_mm_slli_epi16(*this, 7-N)); } + }; + + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 4, "Westmere kernel should use four registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } + + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed + + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::load(ptr+16), simd8::load(ptr+32), simd8::load(ptr+48)} {} + + simdjson_inline void store(T ptr[64]) const { + this->chunks[0].store(ptr+sizeof(simd8)*0); + this->chunks[1].store(ptr+sizeof(simd8)*1); + this->chunks[2].store(ptr+sizeof(simd8)*2); + this->chunks[3].store(ptr+sizeof(simd8)*3); + } + + simdjson_inline simd8 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 mask = simd8::splat(m); + return simd8x64( + 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 &other) const { + return simd8x64( + 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 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] <= mask, + this->chunks[1] <= mask, + this->chunks[2] <= mask, + this->chunks[3] <= mask + ).to_bitmask(); + } + }; // struct simd8x64 + +} // 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 + +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { +#if SIMDJSON_REGULAR_VISUAL_STUDIO + return _addcarry_u64(0, value1, value2, + reinterpret_cast(result)); +#else + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); +#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 + 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(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; + + template> + struct base8: base> { + typedef uint16_t bitmask_t; + typedef uint32_t bitmask2_t; + + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m128i _value) : base>(_value) {} + + friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm_cmpeq_epi8(lhs, rhs); } + + static const int SIZE = sizeof(base>::value); + + template + simdjson_inline simd8 prev(const simd8 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: base8 { + static simdjson_inline simd8 splat(bool _value) { return _mm_set1_epi8(uint8_t(-(!!_value))); } + + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m128i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(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 operator~() const { return *this ^ true; } + }; + + template + struct base8_numeric: base8 { + static simdjson_inline simd8 splat(T _value) { return _mm_set1_epi8(_value); } + static simdjson_inline simd8 zero() { return _mm_setzero_si128(); } + static simdjson_inline simd8 load(const T values[16]) { + return _mm_loadu_si128(reinterpret_cast(values)); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } + + simdjson_inline base8_numeric() : base8() {} + simdjson_inline base8_numeric(const __m128i _value) : base8(_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 operator~() const { return *this ^ 0xFFu; } + + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { return _mm_add_epi8(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return _mm_sub_epi8(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } + + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 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 compress(uint32_t mask) would be + // sensible, but the AVX ISA makes this kind of approach difficult. + template + 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(pshufb_combine_table + pop1 * 8)); + __m128i answer = _mm_shuffle_epi8(pruned, compactmask); + _mm_storeu_si128(reinterpret_cast<__m128i *>(output), answer); + } + + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; + + // Signed bytes + template<> + struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } + + // Order-sensitive comparisons + simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epi8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epi8(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return _mm_cmpgt_epi8(*this, other); } + simdjson_inline simd8 operator<(const simd8 other) const { return _mm_cmpgt_epi8(other, *this); } + }; + + // Unsigned bytes + template<> + struct simd8: base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } + + // Saturated math + simdjson_inline simd8 saturating_add(const simd8 other) const { return _mm_adds_epu8(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm_subs_epu8(*this, other); } + + // Order-specific operations + simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epu8(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epu8(*this, other); } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } + simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } + simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } + simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + simdjson_inline simd8 operator<(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } + simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } + simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } + simdjson_inline simd8 any_bits_set(simd8 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 bits) const { return _mm_testz_si128(*this, bits); } + simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } + template + simdjson_inline simd8 shr() const { return simd8(_mm_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } + template + simdjson_inline simd8 shl() const { return simd8(_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 + simdjson_inline int get_bit() const { return _mm_movemask_epi8(_mm_slli_epi16(*this, 7-N)); } + }; + + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 4, "Westmere kernel should use four registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } + + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed + + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::load(ptr+16), simd8::load(ptr+32), simd8::load(ptr+48)} {} + + simdjson_inline void store(T ptr[64]) const { + this->chunks[0].store(ptr+sizeof(simd8)*0); + this->chunks[1].store(ptr+sizeof(simd8)*1); + this->chunks[2].store(ptr+sizeof(simd8)*2); + this->chunks[3].store(ptr+sizeof(simd8)*3); + } + + simdjson_inline simd8 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 mask = simd8::splat(m); + return simd8x64( + 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 &other) const { + return simd8x64( + 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 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] <= mask, + this->chunks[1] <= mask, + this->chunks[2] <= mask, + this->chunks[3] <= mask + ).to_bitmask(); + } + }; // struct simd8x64 + +} // 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 v0(src); + simd8 v1(src + 16); + v0.store(dst); + v1.store(dst + 16); + uint64_t bs_and_quote = simd8x64(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 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 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 @@ -86684,7 +128720,7 @@ namespace simdjson { /* 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) +#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_BUILDER_DEPENDENCIES_H) #error simdjson/generic/ondemand/dependencies.h must be included before simdjson/generic/ondemand/amalgamated.h! #endif @@ -86699,46 +128735,141 @@ namespace simdjson { /* 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 { +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; +/** 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; +/** @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*; +/** @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; +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; - } // namespace ondemand - } // namespace westmere +} // 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 struct is_builtin_deserializable : std::false_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; + +template +concept is_builtin_deserializable_v = is_builtin_deserializable::value; + +template +concept custom_deserializable = tag_invocable; + +template +concept deserializable = custom_deserializable || is_builtin_deserializable_v || concepts::optional_type; + +template +concept nothrow_custom_deserializable = nothrow_tag_invocable; + +// built-in types are noexcept and if an error happens, the value simply gets ignored and the error is returned. +template +concept nothrow_deserializable = nothrow_custom_deserializable || is_builtin_deserializable_v; + +/// 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 + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(array_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for object + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(object_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for value + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document reference + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + 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 @@ -86750,485 +128881,490 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace ondemand { +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{}; +/** + * 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; +public: + simdjson_inline value_iterator() noexcept = default; - /** - * Denote that we're starting a document. - */ - simdjson_inline void start_document() noexcept; + /** + * 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; + /** + * 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 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 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 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; + /** + * 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; + /** + * 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 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 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 type() 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 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. - * - * @{ - */ + /** + * @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 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 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). - */ - simdjson_warn_unused simdjson_inline simdjson_result 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). - */ - simdjson_warn_unused simdjson_inline simdjson_result started_root_object() noexcept; + /** + * 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 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 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 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 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 has_next_field() 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 has_next_field() noexcept; - /** - * Get the current field's key. - */ - simdjson_warn_unused simdjson_inline simdjson_result field_key() noexcept; + /** + * Get the current field's key. + */ + simdjson_warn_unused simdjson_inline simdjson_result field_key() noexcept; - /** - * Pass the : in the field and move to its value. - */ - simdjson_warn_unused simdjson_inline error_code field_value() 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. + * + * 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 find_field_raw(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 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 find_field_unordered_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 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. - * @{ - */ + /** + * @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 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 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). - */ - simdjson_warn_unused simdjson_inline simdjson_result 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). - */ - simdjson_warn_unused simdjson_inline simdjson_result started_root_array() noexcept; + /** + * 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 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 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 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 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 has_next_element() 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 has_next_element() noexcept; - /** - * Get a child value iterator. - */ - simdjson_warn_unused simdjson_inline value_iterator child() const noexcept; + /** + * Get a child value iterator. + */ + simdjson_warn_unused simdjson_inline value_iterator child() const noexcept; - /** @} */ + /** @} */ - /** - * @defgroup scalar Scalar values - * @addtogroup scalar - * @{ - */ + /** + * @defgroup scalar Scalar values + * @addtogroup scalar + * @{ + */ - simdjson_warn_unused simdjson_inline simdjson_result get_string(bool allow_replacement) noexcept; - template - simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_int64() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_double() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_bool() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_null() noexcept; - simdjson_warn_unused simdjson_inline bool is_negative() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_integer() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_number() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_string(bool allow_replacement) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_int64() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_double() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_bool() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result is_null() noexcept; + simdjson_warn_unused simdjson_inline bool is_negative() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result is_integer() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_number() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_string(bool check_trailing, bool allow_replacement) noexcept; - template - 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 get_root_wobbly_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_raw_json_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64_in_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_int64(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_int64_in_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_double(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_double_in_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_bool(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline bool is_root_negative() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_root_integer(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_number_type(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_number(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_root_null(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_string(bool check_trailing, bool allow_replacement) noexcept; + template + 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 get_root_wobbly_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_raw_json_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64_in_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_int64(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_int64_in_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_double(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_double_in_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_bool(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline bool is_root_negative() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result is_root_integer(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_number_type(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_number(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result is_root_null(bool check_trailing) noexcept; - 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_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; + 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 reset_array() noexcept; - /** - * Restarts an object iteration. - * @returns Whether the object has any fields (returns false for empty). - */ - simdjson_inline simdjson_result 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; + /** @} */ +protected: + /** + * Restarts an array iteration. + * @returns Whether the array has any elements (returns false for empty). + */ + simdjson_inline simdjson_result reset_array() noexcept; + /** + * Restarts an object iteration. + * @returns Whether the object has any fields (returns false for empty). + */ + simdjson_inline simdjson_result 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; + /** + * 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 parse_null(const uint8_t* json) const noexcept; - simdjson_inline simdjson_result 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; + simdjson_inline simdjson_result parse_null(const uint8_t *json) const noexcept; + simdjson_inline simdjson_result 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. - */ + /** + * 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 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_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_inline error_code start_container(uint8_t start_char, const char* incorrect_type_message, const char* type) noexcept; - simdjson_inline error_code end_container() 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 advance_to_value() 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 advance_to_value() noexcept; - simdjson_inline error_code incorrect_type_error(const char* message) const noexcept; - simdjson_inline error_code error_unless_more_tokens(uint32_t tokens = 1) const 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; + 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; + /** + * 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; + 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; + /** 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; - /** @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_inline error_code report_error(error_code error, const char* message) noexcept; + /** @copydoc error_code json_iterator::position() const noexcept; */ + simdjson_inline token_position position() const 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 array; - friend class value; - friend class field; - }; // value_iterator + 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 ondemand +} // namespace westmere } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public westmere::implementation_simdjson_result_base { - 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; - }; +template<> +struct simdjson_result : public westmere::implementation_simdjson_result_base { +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 @@ -87243,776 +129379,876 @@ namespace simdjson { /* 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 */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ +#include + 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; +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, uint64_t, int64_t, double, bool - * - * You may use get_double(), get_bool(), get_uint64(), get_int64(), - * 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 simdjson_inline simdjson_result get() noexcept { - // 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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " 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."); - } + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool + * + * You may use get_double(), get_bool(), get_uint64(), get_int64(), + * 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 + simdjson_inline simdjson_result get() +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } - /** - * Get this value as the given type. - * - * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool - * - * @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 simdjson_inline error_code get(T& out) noexcept; - /** - * 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 get_array() noexcept; + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + #if SIMDJSON_SUPPORTS_CONCEPTS + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else if constexpr (concepts::optional_type) { + using value_type = typename std::remove_cvref_t::value_type; - /** - * 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 get_object() noexcept; + // 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; + } - /** - * 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 get_uint64() noexcept; + if (!out) { + out.emplace(); + } + return get(out.value()); + } else { + static_assert(!sizeof(T), "The get 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 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, double, or bool."); + static_cast(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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " 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(out); // to get rid of unused errors + return UNINITIALIZED; +#endif + } - /** - * 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 get_uint64_in_string() noexcept; + /** + * 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 get_array() 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 get_int64() 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 get_object() 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 get_int64_in_string() 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 get_uint64() 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 get_double() 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 get_uint64_in_string() 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 get_double_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 get_int64() noexcept; - /** - * Cast this JSON value to a string. - * - * The string is guaranteed to be valid UTF-8. - * - * Equivalent to get(). - * - * Important: a value should be consumed once. Calling get_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 get_string(bool allow_replacement = false) 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 get_int64_in_string() noexcept; - /** - * 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 - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) 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 get_double() 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 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 get_raw_json_string() 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 get_double_in_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 get_bool() noexcept; + /** + * Cast this JSON value to a string. + * + * The string is guaranteed to be valid UTF-8. + * + * Equivalent to get(). + * + * 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 get_string(bool allow_replacement = false) 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 is_null() noexcept; + /** + * 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 + 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 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 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 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 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 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 - 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 signed 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(). - * - * @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); + /** + * Cast this JSON value to an instance of type T. The programmer is responsible for + * providing an implementation of get 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 + 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 signed 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(). + * + * @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 begin() & noexcept; - /** - * Sentinel representing the end of the array. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result 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 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 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 at(size_t index) noexcept; - /** - * Look up a field by name on an object (order-sensitive). - * - * 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 }`: - * - * ```c++ - * 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. + /** + * Begin array iteration. + * + * Part of the std::iterable interface. + * + * @returns INCORRECT_TYPE If the JSON value is not an array. + */ + simdjson_inline simdjson_result begin() & noexcept; + /** + * Sentinel representing the end of the array. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result 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 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 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 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 find_field(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ - simdjson_inline simdjson_result find_field(const char* key) noexcept; + * **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 find_field(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ + simdjson_inline simdjson_result 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. - * - * It is the default, however, 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 find_field_unordered(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result find_field_unordered(const char* key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](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 find_field_unordered(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result find_field_unordered(const char *key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result 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 type() noexcept; + /** + * 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 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 is_scalar() noexcept; - /** - * Checks whether the value is a string. - * - * @returns true if the type is string - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". - */ - simdjson_inline simdjson_result is_string() noexcept; + /** + * Checks whether the value is a 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 is_scalar() noexcept; + /** + * Checks whether the value is a string. + * + * @returns true if the type is string + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + */ + simdjson_inline simdjson_result is_string() noexcept; - /** - * Checks whether the value is a negative number. - * - * @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 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 get_number_type() 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 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 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 get_number() 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 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 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 raw_json() 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 raw_json() noexcept; - /** - * Returns the current location in the document if in bounds. - */ - simdjson_inline simdjson_result current_location() noexcept; + /** + * Returns the current location in the document if in bounds. + */ + simdjson_inline simdjson_result 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; + /** + * 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 at_pointer(std::string_view json_pointer) 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 at_pointer(std::string_view json_pointer) noexcept; - /** - * Get the value associated with the given JSONPath expression. We only support - * JSONPath queries that trivially convertible to JSON Pointer queries: key - * names and array indices. - * - * @return The value associated with the given JSONPath expression, or: - * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - */ - simdjson_inline simdjson_result at_path(std::string_view at_path) noexcept; + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + simdjson_inline simdjson_result at_path(std::string_view at_path) noexcept; + /** + * Get all values matching the given JSONPath expression with wildcard support. + * Supports wildcard character (*) for arrays or ".*" for objects. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern + */ + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; - protected: - /** - * Create a value. - */ - simdjson_inline value(const value_iterator& iter) 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; + /** + * 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; + /** + * 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; + /** + * 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 start_or_resume_object() noexcept; + /** + * Get the object, starting or resuming it as necessary + */ + simdjson_inline simdjson_result 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; + // simdjson_inline void log_value(const char *type) const noexcept; + // simdjson_inline void log_error(const char *message) const noexcept; - value_iterator iter{}; + value_iterator iter{}; - friend class document; - friend class array_iterator; - friend class field; - friend class object; - friend struct simdjson_result; - friend struct simdjson_result; - friend class field; - }; + friend class document; + friend class array_iterator; + friend class field; + friend class object; + friend struct simdjson_result; + friend struct simdjson_result; + friend class field; +}; - } // namespace ondemand - } // namespace westmere +} // namespace ondemand +} // namespace westmere } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public westmere::implementation_simdjson_result_base { - 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; +template<> +struct simdjson_result : public westmere::implementation_simdjson_result_base { +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 get_array() noexcept; - simdjson_inline simdjson_result get_object() noexcept; + simdjson_inline simdjson_result get_array() noexcept; + simdjson_inline simdjson_result get_object() noexcept; - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result is_null() noexcept; + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result is_null() noexcept; - template simdjson_inline simdjson_result get() noexcept; + template simdjson_inline simdjson_result get() noexcept; - template simdjson_inline error_code get(T& out) noexcept; + template simdjson_inline error_code get(T &out) noexcept; #if SIMDJSON_EXCEPTIONS - template - 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); + template + 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 count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; + simdjson_inline simdjson_result count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result end() & noexcept; - /** - * Look up a field by name on an object (order-sensitive). - * - * 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 }`: - * - * ```c++ - * 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 find_field(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ - simdjson_inline simdjson_result find_field(const char* key) 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 find_field(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ + simdjson_inline simdjson_result 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. - * - * It is the default, however, 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 find_field_unordered(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result find_field_unordered(const char* key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](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 find_field_unordered(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result find_field_unordered(const char *key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result operator[](int) noexcept = delete; - /** - * Get the type of this JSON value. - * - * 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 type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_string() noexcept; - simdjson_inline simdjson_result is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; + /** + * 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 type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + simdjson_inline simdjson_result is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; - /** @copydoc simdjson_inline std::string_view value::raw_json_token() const noexcept */ - simdjson_inline simdjson_result raw_json_token() noexcept; - simdjson_inline simdjson_result raw_json() noexcept; + /** @copydoc simdjson_inline std::string_view value::raw_json_token() const noexcept */ + simdjson_inline simdjson_result raw_json_token() noexcept; + simdjson_inline simdjson_result raw_json() noexcept; - /** @copydoc simdjson_inline simdjson_result current_location() noexcept */ - simdjson_inline simdjson_result current_location() noexcept; - /** @copydoc simdjson_inline int32_t current_depth() const noexcept */ - simdjson_inline simdjson_result current_depth() const noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - }; + /** @copydoc simdjson_inline simdjson_result current_location() noexcept */ + simdjson_inline simdjson_result current_location() noexcept; + /** @copydoc simdjson_inline int32_t current_depth() const noexcept */ + simdjson_inline simdjson_result current_depth() const noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; +}; } // namespace simdjson @@ -88028,53 +130264,53 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace ondemand { +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 { +// 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 - }; +enum class log_level : int32_t { + info = 0, + error = 1 +}; #if SIMDJSON_VERBOSE_LOGGING - static constexpr const bool LOG_ENABLED = true; + static constexpr const bool LOG_ENABLED = true; #else - static constexpr const bool LOG_ENABLED = false; + 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 - 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 - 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; +// 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 +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 +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_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; +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 logger +} // namespace ondemand +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_LOGGER_H @@ -88091,150 +130327,150 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace ondemand { +namespace westmere { +namespace ondemand { - /** - * Iterates through JSON tokens (`{` `}` `[` `]` `,` `:` `""` `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; +/** + * Iterates through JSON tokens (`{` `}` `[` `]` `,` `:` `""` `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; + /** + * 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; + /** + * 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. + // 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; + 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; +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; + /** + * 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{}; + const uint8_t *buf{}; + token_position _position{}; - friend class json_iterator; - friend class value_iterator; - friend class object; - template - friend simdjson_inline void logger::log_line(const json_iterator& iter, const char* title_prefix, const char* title, std::string_view detail, int delta, int depth_delta, logger::log_level level, Args&&... args) noexcept; - template - 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; - }; + friend class json_iterator; + friend class value_iterator; + friend class object; + template + friend simdjson_inline void logger::log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta, logger::log_level level, Args&&... args) noexcept; + template + 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 ondemand +} // namespace westmere } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public westmere::implementation_simdjson_result_base { - 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 - }; +template<> +struct simdjson_result : public westmere::implementation_simdjson_result_base { +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 @@ -88252,330 +130488,330 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace ondemand { +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 }; +/** + * 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}; - 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; +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 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; + /** + * 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; - /** - * 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; + /** + * 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 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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 depth. + */ + simdjson_inline depth_t depth() const noexcept; - /** - * Get current (writeable) location in the string buffer. - */ - simdjson_inline uint8_t*& string_buf_loc() 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_inline error_code report_error(error_code error, const char* message) 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_inline error_code optional_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; + /** + * 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 unescape(raw_json_string in, bool allow_replacement) noexcept; - simdjson_inline simdjson_result unescape_wobbly(raw_json_string in) 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 unescape(raw_json_string in, bool allow_replacement) noexcept; + simdjson_inline simdjson_result unescape_wobbly(raw_json_string in) noexcept; - simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept; + simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept; - simdjson_inline error_code consume_character(char c) 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; + 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; + /* 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 current_location() const noexcept; + /** + * Returns the current location in the document if in bounds. + */ + inline simdjson_result 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; + /** + * 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; + 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; + /// 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 - 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 - 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 + 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 + 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 + 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 ondemand +} // namespace westmere } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public westmere::implementation_simdjson_result_base { - public: - simdjson_inline simdjson_result(westmere::ondemand::json_iterator&& value) noexcept; ///< @private - simdjson_inline simdjson_result(error_code error) noexcept; ///< @private +template<> +struct simdjson_result : public westmere::implementation_simdjson_result_base { +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; - }; + simdjson_inline simdjson_result() noexcept = default; +}; } // namespace simdjson @@ -88593,152 +130829,153 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace ondemand { +namespace westmere { +namespace ondemand { - /** - * The type of a JSON value. - */ - enum class json_type { - // 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) - }; +/** + * 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 { +/** + * 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 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::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; + /** + * 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; + /** + * 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 - 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 - 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. - */ +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 + 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 + 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 }; - }; + /** + * 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; +/** + * 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& type) noexcept(false); +/** + * 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 &type) noexcept(false); #endif - } // namespace ondemand - } // namespace westmere +} // namespace ondemand +} // namespace westmere } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public westmere::implementation_simdjson_result_base { - 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 - }; +template<> +struct simdjson_result : public westmere::implementation_simdjson_result_base { +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 @@ -88755,200 +130992,206 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace ondemand { +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; +/** + * 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; + /** + * 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; - /** - * 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; + /** + * 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). - * 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 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 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). + * 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 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). + * 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 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; + /** + * 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; - /** - * 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; + /** + * 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; - private: + /** + * 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; } + /** + * 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; } + /** + * 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 unescape(json_iterator& iter, bool allow_replacement) const noexcept; + /** + * 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 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 unescape_wobbly(json_iterator& iter) const noexcept; - const uint8_t* buf{}; - friend class object; - friend class field; - friend class parser; - friend struct simdjson_result; - }; + /** + * 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 unescape_wobbly(json_iterator &iter) const noexcept; + const uint8_t * buf{}; + friend class object; + friend class field; + friend class parser; + friend struct simdjson_result; +}; - simdjson_unused simdjson_inline std::ostream& operator<<(std::ostream&, const raw_json_string&) noexcept; +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; +/** + * 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 ondemand +} // namespace westmere } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public westmere::implementation_simdjson_result_base { - 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 raw() const noexcept; - simdjson_inline simdjson_warn_unused simdjson_result unescape(westmere::ondemand::json_iterator& iter, bool allow_replacement) const noexcept; - simdjson_inline simdjson_warn_unused simdjson_result unescape_wobbly(westmere::ondemand::json_iterator& iter) const noexcept; - }; +template<> +struct simdjson_result : public westmere::implementation_simdjson_result_base { +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 raw() const noexcept; + simdjson_inline char operator[](size_t) const noexcept; + simdjson_inline simdjson_warn_unused simdjson_result unescape(westmere::ondemand::json_iterator &iter, bool allow_replacement) const noexcept; + simdjson_inline simdjson_warn_unused simdjson_result unescape_wobbly(westmere::ondemand::json_iterator &iter) const noexcept; +}; } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H @@ -88964,365 +131207,421 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ #include +#include namespace simdjson { - namespace westmere { - namespace ondemand { +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; +/** + * 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; +/** + * 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; + 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; + /** 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. - * - * @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 iterate(padded_string_view json) & noexcept; + /** + * 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 iterate(padded_string_view json) & noexcept; #ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON - simdjson_warn_unused simdjson_result iterate_allow_incomplete_json(padded_string_view json) & noexcept; + simdjson_warn_unused simdjson_result iterate_allow_incomplete_json(padded_string_view json) & noexcept; #endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const char* json, size_t len, size_t capacity) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const uint8_t* json, size_t len, size_t capacity) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(std::string_view json, size_t capacity) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const std::string& json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(std::string& json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const simdjson_result& json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const simdjson_result& json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(padded_string&& json) & noexcept = delete; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const char *json, size_t len, size_t capacity) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const uint8_t *json, size_t len, size_t capacity) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(std::string_view json, size_t capacity) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const std::string &json) & noexcept; + /** @overload simdjson_result 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 iterate(std::string &json) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const simdjson_result &json) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const simdjson_result &json) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result 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 iterate_raw(padded_string_view json) & noexcept; + /** + * @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 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. - * - * ### 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. - * @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 10MB, which has been a reasonable sweet spot in our tests. - * @param allow_comma_separated (defaults on false) This allows a mode where the documents are - * separated by commas instead of whitespace. It comes with a performance - * penalty because the entire document is indexed at once (and the document must be - * less than 4 GB), and there is no multithreading. In this mode, the batch_size parameter - * is effectively ignored, as it is set to at least the document size. - * @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 iterate_many(const uint8_t* buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ - inline simdjson_result iterate_many(const char* buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ - inline simdjson_result iterate_many(const std::string& s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - inline simdjson_result iterate_many(const std::string&& s, size_t batch_size, bool allow_comma_separated = false) = delete;// unsafe - /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ - inline simdjson_result iterate_many(const padded_string& s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - inline simdjson_result iterate_many(const padded_string&& s, size_t batch_size, bool allow_comma_separated = false) = delete;// unsafe + /** + * 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 10MB, which has been a reasonable sweet spot in our tests. + * @param allow_comma_separated (defaults on false) This allows a mode where the documents are + * separated by commas instead of whitespace. It comes with a performance + * penalty because the entire document is indexed at once (and the document must be + * less than 4 GB), and there is no multithreading. In this mode, the batch_size parameter + * is effectively ignored, as it is set to at least the document size. + * @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 iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(padded_string_view json, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_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 iterate_many(std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @private We do not want to allow implicit conversion from C string to std::string. */ + simdjson_result iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete; - /** @private We do not want to allow implicit conversion from C string to std::string. */ - simdjson_result iterate_many(const char* buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete; + /** 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; - /** 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; - /** - * 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 unescape(raw_json_string in, uint8_t *&dst, bool allow_replacement = false) const 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 }; -#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 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 unescape_wobbly(raw_json_string in, uint8_t*& dst) 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 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; + /** + * 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 - private: - /** @private [for benchmarking access] The implementation to use */ - std::unique_ptr implementation{}; - size_t _capacity{ 0 }; - size_t _max_capacity; - size_t _max_depth{ DEFAULT_MAX_DEPTH }; - std::unique_ptr string_buf{}; + /** + * 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: + friend bool release_parser(); + friend ondemand::parser& get_parser(); + /** Get the thread-local parser instance, allocates it if needed */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_parser_instance(); + /** Get the thread-local parser instance, it might be null */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_threadlocal_parser_if_exists(); + /** @private [for benchmarking access] The implementation to use */ + std::unique_ptr implementation{}; + size_t _capacity{0}; + size_t _max_capacity; + size_t _max_depth{DEFAULT_MAX_DEPTH}; + std::unique_ptr string_buf{}; + #if SIMDJSON_DEVELOPMENT_CHECKS - std::unique_ptr start_positions{}; + std::unique_ptr start_positions{}; #endif - friend class json_iterator; - friend class document_stream; - }; + friend class json_iterator; + friend class document_stream; +}; - } // namespace ondemand - } // namespace westmere +} // namespace ondemand +} // namespace westmere } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public westmere::implementation_simdjson_result_base { - 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; - }; +template<> +struct simdjson_result : public westmere::implementation_simdjson_result_base { +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 @@ -89339,211 +131638,275 @@ namespace simdjson { /* 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 */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace ondemand { +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; +/** + * 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. - */ - simdjson_inline simdjson_result begin() noexcept; - /** - * Sentinel representing the end of the array. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result 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. - * - * To check that an array is empty, it is more performant to use - * the is_empty() method. - */ - simdjson_inline simdjson_result 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 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 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 at_pointer(std::string_view json_pointer) noexcept; + /** + * Begin array iteration. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result begin() noexcept; + /** + * Sentinel representing the end of the array. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result 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 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 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 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 at_pointer(std::string_view json_pointer) noexcept; - /** - * Get the value associated with the given JSONPath expression. We only support - * JSONPath queries that trivially convertible to JSON Pointer queries: key - * names and array indices. - * - * https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00 - * - * @return The value associated with the given JSONPath expression, or: - * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - */ - inline simdjson_result at_path(std::string_view json_path) noexcept; + /** + * 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 at_path(std::string_view json_path) noexcept; - /** - * Consumes the array and returns a string_view instance corresponding to the - * array as represented in JSON. It points inside the original document. - */ - simdjson_inline simdjson_result raw_json() noexcept; + /** + * Get all values matching the given JSONPath expression with wildcard support. + * Supports wildcard patterns like "[*]" to match all array elements. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern + */ + inline simdjson_result> at_path_with_wildcard(std::string_view json_path) 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 at(size_t index) noexcept; - protected: - /** - * Go to the end of the array, no matter where you are right now. - */ - simdjson_inline error_code consume() 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 raw_json() 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 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 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 started(value_iterator& iter) 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 at(size_t index) 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; +#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 + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + 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 + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(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; - /** - * Iterator marking current position. - * - * iter.is_alive() == false indicates iteration is complete. - */ - value_iterator iter{}; + /** + * 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 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 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 started(value_iterator &iter) noexcept; - friend class value; - friend class document; - friend struct simdjson_result; - friend struct simdjson_result; - friend class array_iterator; - }; + /** + * 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; - } // namespace ondemand - } // namespace westmere + /** + * Iterator marking current position. + * + * iter.is_alive() == false indicates iteration is complete. + */ + value_iterator iter{}; + + friend class value; + friend class document; + friend struct simdjson_result; + friend struct simdjson_result; + friend class array_iterator; +}; + +} // namespace ondemand +} // namespace westmere } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public westmere::implementation_simdjson_result_base { - 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; +template<> +struct simdjson_result : public westmere::implementation_simdjson_result_base { +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 begin() noexcept; - simdjson_inline simdjson_result end() noexcept; - inline simdjson_result count_elements() & noexcept; - inline simdjson_result is_empty() & noexcept; - inline simdjson_result reset() & noexcept; - simdjson_inline simdjson_result at(size_t index) noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - simdjson_inline simdjson_result raw_json() noexcept; + simdjson_inline simdjson_result begin() noexcept; + simdjson_inline simdjson_result end() noexcept; + inline simdjson_result count_elements() & noexcept; + inline simdjson_result is_empty() & noexcept; + inline simdjson_result reset() & noexcept; + simdjson_inline simdjson_result at(size_t index) noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h - }; + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(out) ); + } + return SUCCESS; + } +#endif // SIMDJSON_SUPPORTS_CONCEPTS +}; } // namespace simdjson @@ -89555,6 +131918,7 @@ namespace simdjson { /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ /* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H */ +/* amalgamation skipped (editor-only): #include */ /* 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" */ @@ -89562,87 +131926,109 @@ namespace simdjson { namespace simdjson { - namespace westmere { - namespace ondemand { +namespace westmere { +namespace ondemand { - /** - * A forward-only JSON array. - * - * This is an input_iterator, meaning: - * - It is forward-only - * - * must be called exactly once per element. - * - ++ must be called exactly once in between each * (*, ++, *, ++, * ...) - */ - class array_iterator { - public: - /** Create a new, invalid array iterator. */ - simdjson_inline array_iterator() noexcept = default; +/** + * 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; + using difference_type = std::ptrdiff_t; + using pointer = void; + using reference = value_type; - // - // Iterator interface - // + /** Create a new, invalid array iterator. */ + simdjson_inline array_iterator() noexcept = default; - /** - * Get the current element. - * - * Part of the std::iterator interface. - */ - simdjson_inline simdjson_result 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; + // + // Iterator interface + // - private: - value_iterator iter{}; + /** + * Get the current element. + * + * Part of the std::iterator interface. + */ + simdjson_inline simdjson_result + 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; - simdjson_inline array_iterator(const value_iterator& iter) noexcept; + /** + * Check if the array is at the end. + */ + simdjson_warn_unused simdjson_inline bool at_end() const noexcept; - friend class array; - friend class value; - friend struct simdjson_result; - }; +private: +#if SIMDJSON_DEVELOPMENT_CHECKS + bool has_been_referenced{false}; +#endif + value_iterator iter{}; - } // namespace ondemand - } // namespace westmere + simdjson_inline array_iterator(const value_iterator &iter) noexcept; + + friend class array; + friend class value; + friend struct simdjson_result; +}; + +} // namespace ondemand +} // namespace westmere } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public westmere::implementation_simdjson_result_base { - public: - 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; +template<> +struct simdjson_result : public westmere::implementation_simdjson_result_base { + using iterator_category = std::input_iterator_tag; + using value_type = simdjson_result; + using difference_type = std::ptrdiff_t; + using pointer = void; + using reference = value_type; - // - // Iterator interface - // + 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; - simdjson_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. - simdjson_inline bool operator==(const simdjson_result&) const noexcept; - simdjson_inline bool operator!=(const simdjson_result&) const noexcept; - simdjson_inline simdjson_result& operator++() noexcept; - }; + // + // Iterator interface + // + + simdjson_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline bool operator==(const simdjson_result &) const noexcept; + simdjson_inline bool operator!=(const simdjson_result &) const noexcept; + simdjson_inline simdjson_result &operator++() noexcept; + + simdjson_warn_unused simdjson_inline bool at_end() const noexcept; +}; } // namespace simdjson @@ -89656,839 +132042,1050 @@ namespace simdjson { /* 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 */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + namespace simdjson { - namespace westmere { - namespace ondemand { +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; +/** + * 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 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 get_object() & noexcept; - /** - * Cast this JSON value to an unsigned integer. - * - * @returns A signed 64-bit integer. - * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. - */ - simdjson_inline simdjson_result get_uint64() noexcept; - /** - * Cast this JSON value (inside string) to an unsigned integer. - * - * @returns A signed 64-bit integer. - * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. - */ - simdjson_inline simdjson_result 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 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 get_int64_in_string() 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 get_double() noexcept; + /** + * 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 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 get_object() & noexcept; + /** + * Cast this JSON value to an unsigned integer. + * + * @returns A signed 64-bit integer. + * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. + */ + simdjson_inline simdjson_result get_uint64() noexcept; + /** + * Cast this JSON value (inside string) to an unsigned integer. + * + * @returns A signed 64-bit integer. + * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. + */ + simdjson_inline simdjson_result 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 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 get_int64_in_string() 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 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 get_double_in_string() noexcept; - /** - * 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 get_string(bool allow_replacement = false) noexcept; - /** - * 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 - 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 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 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 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 get_value() 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 get_double_in_string() noexcept; + /** + * 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 get_string(bool allow_replacement = false) noexcept; + /** + * 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 + 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 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 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 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 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 is_null() 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 is_null() noexcept; - /** - * Get this value as the given type. - * - * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool - * - * You may use get_double(), get_bool(), get_uint64(), get_int64(), - * 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 simdjson_inline simdjson_result get() & noexcept { - // 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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " 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."); - } - /** @overload template simdjson_result get() & noexcept */ - template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept { - // 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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " 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."); - } + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool + * + * You may use get_double(), get_bool(), get_uint64(), get_int64(), + * 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 + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + /** + * @overload template simdjson_result 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 + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::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(*this).get(); + } - /** - * Get this value as the given type. - * - * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 simdjson_inline error_code get(T& out) & noexcept; - /** @overload template error_code get(T &out) & noexcept */ - template simdjson_deprecated simdjson_inline error_code get(T& out) && noexcept; + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_CONCEPTS + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { + static_assert(!sizeof(T), "The get 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 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, double, or bool."); + static_cast(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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " 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(out); // to get rid of unused errors + return UNINITIALIZED; +#endif // SIMDJSON_SUPPORTS_CONCEPTS + } + + /** @overload template error_code get(T &out) & noexcept */ + template simdjson_deprecated simdjson_inline error_code get(T &out) && noexcept; #if SIMDJSON_EXCEPTIONS - /** - * Cast this JSON value to an instance of type T. The programmer is responsible for - * providing an implementation of get 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 - explicit simdjson_inline operator T() & noexcept(false); - template - explicit simdjson_deprecated simdjson_inline operator T() && noexcept(false); + /** + * Cast this JSON value to an instance of type T. The programmer is responsible for + * providing an implementation of get 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 + explicit simdjson_inline operator T() & noexcept(false); + template + 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 signed 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. - * - * @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); - /** - * 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); + /** + * 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 signed 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. + * + * @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) 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. + */ + 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. + */ + 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. - */ - simdjson_inline simdjson_result 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 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 at(size_t index) & noexcept; - /** - * Begin array iteration. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result begin() & noexcept; - /** - * Sentinel representing the end of the array. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result 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. + */ + simdjson_inline simdjson_result 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 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 at(size_t index) & noexcept; + /** + * Begin array iteration. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result begin() & noexcept; + /** + * Sentinel representing the end of the array. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result end() & noexcept; - /** - * Look up a field by name on an object (order-sensitive). - * - * 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 }`: - * - * ```c++ - * 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 find_field(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result find_field(const char* key) & 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 find_field(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result 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. - * - * It is the default, however, 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 find_field_unordered(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result find_field_unordered(const char* key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](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 find_field_unordered(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result 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). - * - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". - */ - simdjson_inline simdjson_result type() noexcept; + /** + * 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 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 is_scalar() 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 is_scalar() noexcept; - /** - * Checks whether the document is a string. - * - * @returns true if the type is string - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". - */ - simdjson_inline simdjson_result is_string() noexcept; + /** + * Checks whether the document is a string. + * + * @returns true if the type is string + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + */ + simdjson_inline simdjson_result is_string() noexcept; - /** - * Checks whether the document is a negative number. - * - * @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 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 get_number_type() 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 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 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 get_number() 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 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 raw_json_token() 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 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; + /** + * 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 current_location() const noexcept; + /** + * Returns the current location in the document if in bounds. + */ + inline simdjson_result 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 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; + /** + * 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 at_pointer(std::string_view json_pointer) 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 at_pointer(std::string_view json_pointer) noexcept; - /** - * Get the value associated with the given JSONPath expression. We only support - * JSONPath queries that trivially convertible to JSON Pointer queries: key - * names and array indices. - * - * https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00 - * - * Key values are matched exactly, without unescaping or Unicode normalization. - * We do a byte-by-byte comparison. E.g. - * - * const padded_string json = "{\"\\u00E9\":123}"_padded; - * auto doc = parser.iterate(json); - * doc.at_path(".\\u00E9") == 123 - * doc.at_path((const char*)u8".\u00E9") returns an error (NO_SUCH_FIELD) - * - * @return The value associated with the given JSONPath expression, or: - * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - */ - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + /** + * 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 at_path(std::string_view json_path) noexcept; - /** - * Consumes the document and returns a string_view instance corresponding to the - * document as represented in JSON. It points inside the original byte array containing - * the JSON document. - */ - simdjson_inline simdjson_result raw_json() noexcept; - protected: - /** - * Consumes the document. - */ - simdjson_inline error_code consume() noexcept; + /** + * Get all values matching the given JSONPath expression with wildcard support. + * + * Supports wildcard patterns like "$.array[*]" or "$.object.*" to match multiple elements. + * + * This method materializes all matching values into a vector. + * The document will be consumed after this call. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern, or: + * - INVALID_JSON_POINTER if the JSONPath cannot be parsed + * - NO_SUCH_FIELD if a field does not exist + * - INDEX_OUT_OF_BOUNDS if an array index is out of bounds + * - INCORRECT_TYPE if path traversal encounters wrong type + */ + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; - simdjson_inline document(ondemand::json_iterator&& iter) noexcept; - simdjson_inline const uint8_t* text(uint32_t idx) const 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 raw_json() noexcept; - simdjson_inline value_iterator resume_value_iterator() noexcept; - simdjson_inline value_iterator get_root_value_iterator() noexcept; - simdjson_inline simdjson_result start_or_resume_object() noexcept; - static simdjson_inline document start(ondemand::json_iterator&& iter) 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 + requires(std::is_class_v && (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; - // - // Fields - // - json_iterator iter{}; ///< Current position in the document - static constexpr depth_t DOCUMENT_DEPTH = 0; ///< document depth is always 0 + simdjson_inline document(ondemand::json_iterator &&iter) noexcept; + simdjson_inline const uint8_t *text(uint32_t idx) const noexcept; - 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; - }; + simdjson_inline value_iterator resume_value_iterator() noexcept; + simdjson_inline value_iterator get_root_value_iterator() noexcept; + simdjson_inline simdjson_result 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. - */ - 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 get_array() & noexcept; - simdjson_inline simdjson_result get_object() & noexcept; - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result get_value() noexcept; +/** + * 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 get_array() & noexcept; + simdjson_inline simdjson_result get_object() & noexcept; + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result get_value() noexcept; - simdjson_inline simdjson_result is_null() noexcept; - template simdjson_inline simdjson_result get() & noexcept; - simdjson_inline simdjson_result raw_json() noexcept; - simdjson_inline operator document& () const noexcept; + simdjson_inline simdjson_result is_null() noexcept; + template + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + template + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::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(*this).get(); + } + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_CONCEPTS + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { + static_assert(!sizeof(T), "The get 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 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, double, or bool."); + static_cast(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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " 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(out); // to get rid of unused errors + return UNINITIALIZED; +#endif // SIMDJSON_SUPPORTS_CONCEPTS + } + + /** @overload template error_code get(T &out) & noexcept */ + template simdjson_inline error_code get(T &out) && noexcept; + simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (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 - explicit simdjson_inline operator T() noexcept(false); - simdjson_inline operator array() & noexcept(false); - simdjson_inline operator 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 raw_json_string() noexcept(false); - simdjson_inline operator bool() noexcept(false); - simdjson_inline operator value() noexcept(false); + template + explicit simdjson_inline operator T() noexcept(false); + simdjson_inline operator array() & noexcept(false); + simdjson_inline operator 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 raw_json_string() noexcept(false); + simdjson_inline operator bool() noexcept(false); + simdjson_inline operator value() noexcept(false); #endif - simdjson_inline simdjson_result count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) & noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(const char* key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](const char* key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(const char* key) & noexcept; + simdjson_inline simdjson_result count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) & noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result end() & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(const char *key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; - simdjson_inline simdjson_result type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_string() noexcept; + simdjson_inline simdjson_result type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; - simdjson_inline simdjson_result current_location() noexcept; - simdjson_inline int32_t current_depth() const noexcept; - simdjson_inline bool is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - simdjson_inline simdjson_result raw_json_token() noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result current_location() noexcept; + simdjson_inline int32_t current_depth() const noexcept; + simdjson_inline bool is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + simdjson_inline simdjson_result raw_json_token() noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; - private: - document* doc{ nullptr }; - }; - } // namespace ondemand - } // namespace westmere +private: + document *doc{nullptr}; +}; +} // namespace ondemand +} // namespace westmere } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public westmere::implementation_simdjson_result_base { - 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; +template<> +struct simdjson_result : public westmere::implementation_simdjson_result_base { +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 get_array() & noexcept; - simdjson_inline simdjson_result get_object() & noexcept; - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result get_value() noexcept; - simdjson_inline simdjson_result is_null() noexcept; + simdjson_inline simdjson_result get_array() & noexcept; + simdjson_inline simdjson_result get_object() & noexcept; + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result get_value() noexcept; + simdjson_inline simdjson_result is_null() noexcept; - template simdjson_inline simdjson_result get() & noexcept; - template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept; + template simdjson_inline simdjson_result get() & noexcept; + template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept; - template simdjson_inline error_code get(T& out) & noexcept; - template simdjson_inline error_code get(T& out) && noexcept; + template simdjson_inline error_code get(T &out) & noexcept; + template simdjson_inline error_code get(T &out) && noexcept; #if SIMDJSON_EXCEPTIONS - template ::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); - 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); - simdjson_inline operator westmere::ondemand::value() noexcept(false); -#endif - simdjson_inline simdjson_result count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) & noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(const char* key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](const char* key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(const char* key) & noexcept; - simdjson_inline simdjson_result type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_string() noexcept; - simdjson_inline simdjson_result current_location() noexcept; - simdjson_inline int32_t current_depth() const noexcept; - simdjson_inline bool at_end() const noexcept; - simdjson_inline bool is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - /** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */ - simdjson_inline simdjson_result raw_json_token() noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - }; + using westmere::implementation_simdjson_result_base::operator*; + using westmere::implementation_simdjson_result_base::operator->; + template ::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); + 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); + simdjson_inline operator westmere::ondemand::value() noexcept(false); +#endif + simdjson_inline simdjson_result count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) & noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result end() & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(const char *key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + simdjson_inline simdjson_result type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + simdjson_inline simdjson_result current_location() noexcept; + simdjson_inline int32_t current_depth() const noexcept; + simdjson_inline bool at_end() const noexcept; + simdjson_inline bool is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + /** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */ + simdjson_inline simdjson_result raw_json_token() noexcept; + + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION +}; } // namespace simdjson @@ -90497,68 +133094,81 @@ namespace simdjson { namespace simdjson { - template<> - struct simdjson_result : public westmere::implementation_simdjson_result_base { - 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; +template<> +struct simdjson_result : public westmere::implementation_simdjson_result_base { +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 get_array() & noexcept; - simdjson_inline simdjson_result get_object() & noexcept; - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result get_value() noexcept; - simdjson_inline simdjson_result is_null() noexcept; + simdjson_inline simdjson_result get_array() & noexcept; + simdjson_inline simdjson_result get_object() & noexcept; + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result get_value() noexcept; + simdjson_inline simdjson_result is_null() noexcept; + + template simdjson_inline simdjson_result get() & noexcept; + template simdjson_inline simdjson_result get() && noexcept; + + template simdjson_inline error_code get(T &out) & noexcept; + template simdjson_inline error_code get(T &out) && noexcept; #if SIMDJSON_EXCEPTIONS - template ::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); - 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); - simdjson_inline operator westmere::ondemand::value() noexcept(false); + template + 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); + simdjson_inline operator westmere::ondemand::value() noexcept(false); #endif - simdjson_inline simdjson_result count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) & noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(const char* key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](const char* key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(const char* key) & noexcept; - simdjson_inline simdjson_result type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_string() noexcept; - simdjson_inline simdjson_result current_location() noexcept; - simdjson_inline simdjson_result current_depth() const noexcept; - simdjson_inline simdjson_result is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - /** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */ - simdjson_inline simdjson_result raw_json_token() noexcept; + simdjson_inline simdjson_result count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) & noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result end() & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(const char *key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + simdjson_inline simdjson_result type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + simdjson_inline simdjson_result current_location() noexcept; + simdjson_inline simdjson_result current_depth() const noexcept; + simdjson_inline simdjson_result is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + /** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */ + simdjson_inline simdjson_result raw_json_token() noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - }; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION +}; } // namespace simdjson @@ -90584,322 +133194,330 @@ namespace simdjson { #endif namespace simdjson { - namespace westmere { - namespace ondemand { +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 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: +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(); + /** + * 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 }; + 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{}; + /** + * We lock using a mutex. + */ + std::mutex locking_mutex{}; + std::condition_variable cond_var{}; - friend class document_stream; - }; + 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. - * - * ```c++ - * 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; +/** + * 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; + simdjson_inline ~document_stream() noexcept; - /** - * Returns the input size in bytes. - */ - inline size_t size_in_bytes() const 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(); - * - */ - inline size_t truncated_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(); + * + */ + inline size_t truncated_bytes() const noexcept; - class iterator { - public: - using value_type = simdjson_result; - using reference = simdjson_result; - using pointer = void; - using difference_type = std::ptrdiff_t; - using iterator_category = std::input_iterator_tag; + class iterator { + public: + using value_type = simdjson_result; + using reference = simdjson_result; + using pointer = void; + 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; + /** + * 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. - * - * 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 + * + * 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. + * + * 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 error of the stream (if any). + */ + inline error_code error() 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; + /** + * Returns whether the iterator is at the end. + */ + inline bool at_end() const noexcept; - friend class document; - friend class document_stream; - friend class json_iterator; - }; + 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; - /** - * 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; + friend class document; + friend class document_stream; + friend class json_iterator; + }; + using iterator = document_stream::iterator; - private: + /** + * 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; - document_stream& operator=(const document_stream&) = delete; // Disallow copying - document_stream(const document_stream& other) = delete; // Disallow copying +private: - /** - * 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) - */ - simdjson_inline document_stream( - ondemand::parser& parser, - const uint8_t* buf, - size_t len, - size_t batch_size, - bool allow_comma_separated - ) noexcept; + document_stream &operator=(const document_stream &) = delete; // Disallow copying + document_stream(const document_stream &other) = delete; // Disallow copying - /** - * Parse the first document in the buffer. Used by begin(), to handle allocation and - * initialization. - */ - inline void start() noexcept; + /** + * 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) + */ + simdjson_inline document_stream( + ondemand::parser &parser, + const uint8_t *buf, + size_t len, + size_t batch_size, + bool allow_comma_separated + ) 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; + /** + * Parse the first document in the buffer. Used by begin(), to handle allocation and + * initialization. + */ + inline void start() noexcept; - /** Move the json_iterator of the document to the location of the next document in the stream. */ - inline void next_document() 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; - /** Get the next document index. */ - inline size_t next_batch_start() const noexcept; + /** Move the json_iterator of the document to the location of the next document in the stream. */ + inline void next_document() 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; + /** Get the next document index. */ + inline size_t next_batch_start() const noexcept; - // Fields - ondemand::parser* parser; - const uint8_t* buf; - size_t len; - size_t batch_size; - bool allow_comma_separated; - /** - * 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{}; + /** Pass the next batch through stage 1 with the given parser. */ + inline error_code run_stage1(ondemand::parser &p, size_t batch_start) noexcept; -#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; + // Fields + ondemand::parser *parser; + const uint8_t *buf; + size_t len; + size_t batch_size; + bool allow_comma_separated; + /** + * 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{}; - inline void load_from_stage1_thread() noexcept; + #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; - /** Start a thread to run stage 1 on the next batch. */ - inline void start_stage1_thread() noexcept; + inline void load_from_stage1_thread() noexcept; - /** Wait for the stage 1 thread to finish and capture the results. */ - inline void finish_stage1_thread() noexcept; + /** Start a thread to run stage 1 on the next batch. */ + inline void start_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 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. - */ - ondemand::parser stage1_thread_parser{}; + /** Wait for the stage 1 thread to finish and capture the results. */ + inline void finish_stage1_thread() noexcept; - friend struct stage1_worker; -#endif // SIMDJSON_THREADS_ENABLED + /** 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 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. + */ + ondemand::parser stage1_thread_parser{}; - friend class parser; - friend class document; - friend class json_iterator; - friend struct simdjson_result; - friend struct simdjson::internal::simdjson_result_base; - }; // document_stream + friend struct stage1_worker; + #endif // SIMDJSON_THREADS_ENABLED - } // namespace ondemand - } // namespace westmere + friend class parser; + friend class document; + friend class json_iterator; + friend struct simdjson_result; + friend struct simdjson::internal::simdjson_result_base; +}; // document_stream + +} // namespace ondemand +} // namespace westmere } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public westmere::implementation_simdjson_result_base { - 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; - }; +template<> +struct simdjson_result : public westmere::implementation_simdjson_result_base { +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 @@ -90918,104 +133536,104 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace ondemand { +namespace westmere { +namespace ondemand { - /** - * A JSON field (key/value pair) in an object. - * - * Returned from object iteration. - * - * Extends from std::pair so you can use C++ algorithms that rely on pairs. - */ - class field : public std::pair { - public: - /** - * Create a new invalid field. - * - * Exists so you can declare a variable and later assign to it before use. - */ - simdjson_inline field() noexcept; +/** + * A JSON field (key/value pair) in an object. + * + * Returned from object iteration. + * + * Extends from std::pair so you can use C++ algorithms that rely on pairs. + */ +class field : public std::pair { +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 unescaped_key(bool allow_replacement = false) 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. 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 - 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; - /** - * Get the field value. - */ - simdjson_inline ondemand::value& value() & noexcept; - /** - * @overload ondemand::value &ondemand::value() & noexcept - */ - simdjson_inline ondemand::value value() && 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 unescaped_key(bool allow_replacement = false) 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. 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 + 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; + /** + * 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 start(value_iterator& parent_iter) noexcept; - static simdjson_inline simdjson_result start(const value_iterator& parent_iter, raw_json_string key) noexcept; - friend struct simdjson_result; - friend class object_iterator; - }; +protected: + simdjson_inline field(raw_json_string key, ondemand::value &&value) noexcept; + static simdjson_inline simdjson_result start(value_iterator &parent_iter) noexcept; + static simdjson_inline simdjson_result start(const value_iterator &parent_iter, raw_json_string key) noexcept; + friend struct simdjson_result; + friend class object_iterator; +}; - } // namespace ondemand - } // namespace westmere +} // namespace ondemand +} // namespace westmere } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public westmere::implementation_simdjson_result_base { - 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; +template<> +struct simdjson_result : public westmere::implementation_simdjson_result_base { +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 unescaped_key(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code unescaped_key(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result key() noexcept; - simdjson_inline simdjson_result key_raw_json_token() noexcept; - simdjson_inline simdjson_result escaped_key() noexcept; - simdjson_inline simdjson_result value() noexcept; - }; + simdjson_inline simdjson_result unescaped_key(bool allow_replacement = false) noexcept; + template + simdjson_inline error_code unescaped_key(string_type &receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result key() noexcept; + simdjson_inline simdjson_result key_raw_json_token() noexcept; + simdjson_inline simdjson_result escaped_key() noexcept; + simdjson_inline simdjson_result value() noexcept; +}; } // namespace simdjson @@ -91030,253 +133648,367 @@ namespace simdjson { /* 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 */ +/* 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 { +namespace westmere { +namespace ondemand { - /** - * 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; +/** + * 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; - simdjson_inline simdjson_result begin() noexcept; - simdjson_inline simdjson_result end() noexcept; - /** - * Look up a field by name on an object (order-sensitive). - * - * 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 }`: - * - * ```c++ - * 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 }`. - * - * 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 find_field(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result find_field(std::string_view key) && noexcept; + /** + * 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 + */ + simdjson_inline simdjson_result begin() noexcept; + simdjson_inline simdjson_result 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"); + * ``` + * 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 find_field(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result 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. - * - * It is the default, however, 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 find_field_unordered(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](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 find_field_unordered(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) && 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("/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 at_pointer(std::string_view json_pointer) 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("/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 at_pointer(std::string_view json_pointer) noexcept; - /** - * Get the value associated with the given JSONPath expression. We only support - * JSONPath queries that trivially convertible to JSON Pointer queries: key - * names and array indices. - * - * @return The value associated with the given JSONPath expression, or: - * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - */ - inline simdjson_result at_path(std::string_view json_path) noexcept; + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + inline simdjson_result at_path(std::string_view json_path) noexcept; - /** - * Reset the iterator so that we are pointing back at the - * beginning of the object. You should still consume values only once even if you - * 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 reset() & 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 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 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 raw_json() noexcept; + /** + * Get all values matching the given JSONPath expression with wildcard support. + * Supports wildcard patterns like ".*" to match all object fields. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern + */ + inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; - protected: - /** - * Go to the end of the object, no matter where you are right now. - */ - simdjson_inline error_code consume() noexcept; - static simdjson_inline simdjson_result start(value_iterator& iter) noexcept; - static simdjson_inline simdjson_result start_root(value_iterator& iter) noexcept; - static simdjson_inline simdjson_result started(value_iterator& iter) noexcept; - static simdjson_inline object resume(const value_iterator& iter) noexcept; - simdjson_inline object(const value_iterator& iter) 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 reset() & 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 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 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 raw_json() noexcept; - simdjson_warn_unused simdjson_inline error_code find_field_raw(const std::string_view key) 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + 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 + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } - value_iterator iter{}; +#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 + requires(std::is_class_v && (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 start(value_iterator &iter) noexcept; + static simdjson_inline simdjson_result start_root(value_iterator &iter) noexcept; + static simdjson_inline simdjson_result started(value_iterator &iter) noexcept; + static simdjson_inline object resume(const value_iterator &iter) noexcept; + simdjson_inline object(const value_iterator &iter) noexcept; - friend class value; - friend class document; - friend struct simdjson_result; - }; + simdjson_warn_unused simdjson_inline error_code find_field_raw(const std::string_view key) noexcept; - } // namespace ondemand - } // namespace westmere + value_iterator iter{}; + + friend class value; + friend class document; + friend struct simdjson_result; +}; + +} // namespace ondemand +} // namespace westmere } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public westmere::implementation_simdjson_result_base { - 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; +template<> +struct simdjson_result : public westmere::implementation_simdjson_result_base { +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 begin() noexcept; - simdjson_inline simdjson_result end() noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) && noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result begin() noexcept; + simdjson_inline simdjson_result end() noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) && noexcept; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + inline simdjson_result reset() noexcept; + inline simdjson_result is_empty() noexcept; + inline simdjson_result count_fields() & noexcept; + inline simdjson_result raw_json() noexcept; + #if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h - inline simdjson_result reset() noexcept; - inline simdjson_result is_empty() noexcept; - inline simdjson_result count_fields() & noexcept; - inline simdjson_result raw_json() noexcept; + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(out) ); + } + return SUCCESS; + } - }; +#if SIMDJSON_STATIC_REFLECTION + // TODO: move this code into object-inl.h + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) noexcept { + if (error()) { return error(); } + return first.extract_into(out); + } +#endif // SIMDJSON_STATIC_REFLECTION +#endif // SIMDJSON_SUPPORTS_CONCEPTS +}; } // namespace simdjson @@ -91294,72 +134026,77 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace ondemand { +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; +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; - // - // Iterator interface - // + // + // Iterator interface + // - // Reads key and value, yielding them to the user. - // MUST ONLY BE CALLED ONCE PER ITERATION. - simdjson_inline simdjson_result 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 ',' - simdjson_inline object_iterator& operator++() noexcept; + // Reads key and value, yielding them to the user. + // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline simdjson_result 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: - /** - * 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{}; +private: +#if SIMDJSON_DEVELOPMENT_CHECKS + bool has_been_referenced{false}; +#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; - friend class object; - }; + simdjson_inline object_iterator(const value_iterator &iter) noexcept; + friend struct simdjson_result; + friend class object; +}; - } // namespace ondemand - } // namespace westmere +} // namespace ondemand +} // namespace westmere } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public westmere::implementation_simdjson_result_base { - 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; +template<> +struct simdjson_result : public westmere::implementation_simdjson_result_base { +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 - // + // + // Iterator interface + // - // Reads key and value, yielding them to the user. - simdjson_inline simdjson_result 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&) const noexcept; - // Assumes it's being compared with the end. true if depth >= iter->depth. - simdjson_inline bool operator!=(const simdjson_result&) const noexcept; - // Checks for ']' and ',' - simdjson_inline simdjson_result& operator++() noexcept; - }; + // Reads key and value, yielding them to the user. + simdjson_inline simdjson_result 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 &) const noexcept; + // Assumes it's being compared with the end. true if depth >= iter->depth. + simdjson_inline bool operator!=(const simdjson_result &) const noexcept; + // Checks for ']' and ',' + simdjson_inline simdjson_result &operator++() noexcept; +}; } // namespace simdjson @@ -91375,35 +134112,49 @@ namespace simdjson { /* 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 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 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 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 to_json_string(westmere::ondemand::array& x) noexcept; - inline simdjson_result to_json_string(simdjson_result x); - inline simdjson_result to_json_string(simdjson_result x); - inline simdjson_result to_json_string(simdjson_result x); - inline simdjson_result to_json_string(simdjson_result x); +/** + * 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 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 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 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 to_json_string(westmere::ondemand::array& x) noexcept; +inline simdjson_result to_json_string(simdjson_result x); +inline simdjson_result to_json_string(simdjson_result x); +inline simdjson_result to_json_string(simdjson_result x); +inline simdjson_result to_json_string(simdjson_result 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 + requires(!std::same_as && + !std::same_as && + !std::same_as && + !std::same_as) +inline std::string to_json_string(const T& obj); +#endif + } // namespace simdjson /** @@ -91413,69 +134164,652 @@ namespace simdjson { * Credit: @madhur4127 * See https://github.com/simdjson/simdjson/issues/1768 */ -namespace simdjson { - namespace westmere { - namespace ondemand { +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); +/** + * 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 x); +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result 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); +/** + * 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 x); +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result 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); +/** + * 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&& x); +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& x); #endif - inline std::ostream& operator<<(std::ostream& out, simdjson::westmere::ondemand::document_reference& value); +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&& x); +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& 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); +/** + * 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 x); +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x); #endif - } - } -} // namespace simdjson::westmere::ondemand +}}} // 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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +#include +#if SIMDJSON_STATIC_REFLECTION +#include +// #include // for std::define_static_string - header not available yet +#endif + +namespace simdjson { + +////////////////////////////// +// Number deserialization +////////////////////////////// + +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + uint64_t x; + SIMDJSON_TRY(val.get_uint64().get(x)); + if (x > (limits::max)()) { + return NUMBER_OUT_OF_RANGE; + } + out = static_cast(x); + return SUCCESS; +} + +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + double x; + SIMDJSON_TRY(val.get_double().get(x)); + out = static_cast(x); + return SUCCESS; +} + +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + 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(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 +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v) { + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + out = T{str}; + return SUCCESS; +} + + +/** + * 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>(). + */ +template +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { + using value_type = typename std::remove_cvref_t::value_type; + static_assert( + deserializable, + "The specified type inside the container must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the container must default constructible."); + westmere::ondemand::array arr; + if constexpr (std::is_same_v, westmere::ondemand::array>) { + arr = val; + } else { + SIMDJSON_TRY(val.get_array().get(arr)); + } + + for (auto v : arr) { + if constexpr (concepts::returns_reference) { + if (auto const err = v.get().get(concepts::emplace_one(out)); + err) { + // If an error occurs, the empty element that we just inserted gets + // removed. We're not using a temp variable because if T is a heavy + // type, we want the valid path to be the fast path and the slow path be + // the path that has errors in it. + if constexpr (requires { out.pop_back(); }) { + static_cast(out.pop_back()); + } + return err; + } + } else { + value_type temp; + if (auto const err = v.get().get(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 +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { + using value_type = typename std::remove_cvref_t::mapped_type; + static_assert( + deserializable, + "The specified value type inside the container must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "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().get(this_value)); + [[maybe_unused]] std::pair 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 +error_code tag_invoke(deserialize_tag, westmere::ondemand::object &obj, T &out) noexcept { + using value_type = typename std::remove_cvref_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 +error_code tag_invoke(deserialize_tag, westmere::ondemand::value &val, T &out) noexcept { + westmere::ondemand::object obj; + SIMDJSON_TRY(val.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} + +template +error_code tag_invoke(deserialize_tag, westmere::ondemand::document &doc, T &out) noexcept { + westmere::ondemand::object obj; + SIMDJSON_TRY(doc.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} + +template +error_code tag_invoke(deserialize_tag, westmere::ondemand::document_reference &doc, T &out) noexcept { + 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. + * + * If constructing T is nothrow, this conversion should be nothrow as well since + * we return MEMALLOC if we're not able to allocate memory instead of throwing + * the error message. + * + * @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 +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::element_type, ValT>) { + using element_type = typename std::remove_cvref_t::element_type; + + // For better error messages, don't use these as constraints on + // the tag_invoke CPO. + static_assert( + deserializable, + "The specified type inside the unique_ptr must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the unique_ptr must default constructible."); + + auto ptr = new (std::nothrow) element_type(); + if (ptr == nullptr) { + return MEMALLOC; + } + SIMDJSON_TRY(val.template get(*ptr)); + out.reset(ptr); + return SUCCESS; +} + +/** + * This CPO (Customization Point Object) will help deserialize into optional types. + */ +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable::value_type, decltype(val)>) { + using value_type = typename std::remove_cvref_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(out.value())); + return SUCCESS; +} + + +#if SIMDJSON_STATIC_REFLECTION + + +template +constexpr bool user_defined_type = (std::is_class_v +&& !std::is_same_v && !std::is_same_v && !concepts::optional_type && +!concepts::appendable_containers); + + +template + requires(user_defined_type && std::is_class_v) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { + westmere::ondemand::object obj; + if constexpr (std::is_same_v, westmere::ondemand::object>) { + obj = val; + } else { + SIMDJSON_TRY(val.get_object().get(obj)); + } + 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)); + if constexpr (concepts::optional_type) { + // for optional members, it's ok if the key is missing + auto error = obj[key].get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + if(error == NO_SUCH_FIELD) { + out.[:mem:].reset(); + continue; + } + return error; + } + } else { + // for non-optional members, the key must be present + SIMDJSON_TRY(obj[key].get(out.[:mem:])); + } + } + }; + return simdjson::SUCCESS; +} + +// Support for enum deserialization - deserialize from string representation using expand approach from P2996R12 +template + requires(std::is_enum_v) +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)); + constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T)); + template for (constexpr auto enum_val : enumerators) { + if (str == std::meta::identifier_of(enum_val)) { + out = [:enum_val:]; + return SUCCESS; + } + }; + + return INCORRECT_TYPE; +#else + // Fallback: deserialize as integer if reflection not available + std::underlying_type_t int_val; + SIMDJSON_TRY(val.get(int_val)); + out = static_cast(int_val); + return SUCCESS; +#endif +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr &out) noexcept { + if (!out) { + out = std::make_unique(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr &out) noexcept { + if (!out) { + out = std::make_shared(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +#endif // SIMDJSON_STATIC_REFLECTION + +//////////////////////////////////////// +// Unique pointers +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + 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 &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + 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 &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_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 &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_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 &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(); + } + int64_t temp; + SIMDJSON_TRY(val.get_int64().get(temp)); + *out = static_cast(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 */ @@ -91483,6 +134817,7 @@ namespace simdjson { /* 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" */ @@ -91492,279 +134827,291 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace ondemand { +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. - // +// +// ### 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 array::array(const value_iterator &_iter) noexcept + : iter{_iter} +{ +} - simdjson_inline simdjson_result 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::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::started(value_iterator& iter) noexcept { - bool has_value; - SIMDJSON_TRY(iter.started_array().get(has_value)); - return array(iter); - } +simdjson_inline simdjson_result 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::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::started(value_iterator &iter) noexcept { + bool has_value; + SIMDJSON_TRY(iter.started_array().get(has_value)); + return array(iter); +} - simdjson_inline simdjson_result array::begin() noexcept { +simdjson_inline simdjson_result array::begin() noexcept { #if SIMDJSON_DEVELOPMENT_CHECKS - if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } + if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } #endif - return array_iterator(iter); - } - simdjson_inline simdjson_result array::end() noexcept { - return array_iterator(iter); - } - simdjson_inline error_code array::consume() noexcept { - auto error = iter.json_iter().skip_child(iter.depth() - 1); - if (error) { iter.abandon(); } - return error; - } + return array_iterator(iter); +} +simdjson_inline simdjson_result 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 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(starting_point), size_t(final_point - starting_point)); - } +simdjson_inline simdjson_result 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(starting_point), size_t(final_point - starting_point)); +} - SIMDJSON_PUSH_DISABLE_WARNINGS - SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING - simdjson_inline simdjson_result array::count_elements() & 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 array after counting the number of elements. - iter.reset_array(); - return count; - } - SIMDJSON_POP_DISABLE_WARNINGS +SIMDJSON_PUSH_DISABLE_WARNINGS +SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING +simdjson_inline simdjson_result array::count_elements() & 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 array after counting the number of elements. + iter.reset_array(); + return count; +} +SIMDJSON_POP_DISABLE_WARNINGS - simdjson_inline simdjson_result array::is_empty() & noexcept { - bool is_not_empty; - auto error = iter.reset_array().get(is_not_empty); - if (error) { return error; } - return !is_not_empty; - } +simdjson_inline simdjson_result array::is_empty() & noexcept { + bool is_not_empty; + auto error = iter.reset_array().get(is_not_empty); + if(error) { return error; } + return !is_not_empty; +} - inline simdjson_result array::reset() & noexcept { - return iter.reset_array(); - } +inline simdjson_result array::reset() & noexcept { + return iter.reset_array(); +} - inline simdjson_result array::at_pointer(std::string_view json_pointer) noexcept { - if (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; } +inline simdjson_result array::at_pointer(std::string_view json_pointer) noexcept { + if (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; } - // Read the array index - size_t array_index = 0; - size_t i; - for (i = 0; i < json_pointer.length() && json_pointer[i] != '/'; i++) { - uint8_t digit = uint8_t(json_pointer[i] - '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; } - array_index = array_index * 10 + digit; - } + // Read the array index + size_t array_index = 0; + size_t i; + for (i = 0; i < json_pointer.length() && json_pointer[i] != '/'; i++) { + uint8_t digit = uint8_t(json_pointer[i] - '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; } + array_index = array_index*10 + digit; + } - // 0 followed by other digits is invalid - if (i > 1 && json_pointer[0] == '0') { return INVALID_JSON_POINTER; } // "JSON pointer array index has other characters after 0" + // 0 followed by other digits is invalid + if (i > 1 && json_pointer[0] == '0') { return INVALID_JSON_POINTER; } // "JSON pointer array index has other characters after 0" - // Empty string is invalid; so is a "/" with no digits before it - if (i == 0) { return INVALID_JSON_POINTER; } // "Empty string in JSON pointer array index" - // Get the child - auto child = at(array_index); - // If there is an error, it ends here - if (child.error()) { - return child; - } + // Empty string is invalid; so is a "/" with no digits before it + if (i == 0) { return INVALID_JSON_POINTER; } // "Empty string in JSON pointer array index" + // 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; - } + // 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 std::string json_path_to_pointer_conversion(std::string_view json_path) { - if (json_path.empty() || (json_path.front() != '.' && - json_path.front() != '[')) { - return "-1"; // This is just a sentinel value, the caller should check for this and return an error. - } +inline simdjson_result array::at_path(std::string_view json_path) noexcept { + auto json_pointer = json_path_to_pointer_conversion(json_path); + if (json_pointer == "-1") { return INVALID_JSON_POINTER; } + return at_pointer(json_pointer); +} - std::string result; - // Reserve space to reduce allocations, adjusting for potential increases due - // to escaping. - result.reserve(json_path.size() * 2); +inline simdjson_result> array::at_path_with_wildcard(std::string_view json_path) noexcept { + std::vector result; - size_t i = 0; + 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; + // Wildcard case + if(key=="*"){ + for(auto element: *this){ - 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; - } + if(element.error()){ + return element.error(); + } - return result; - } + if(remaining_path.empty()){ + // Use value_unsafe() because we've already checked for errors above. + // The 'element' is a simdjson_result wrapper, and we need to extract + // the underlying value. value_unsafe() is safe here because error() returned false. + result.push_back(std::move(element).value_unsafe()); - inline simdjson_result array::at_path(std::string_view json_path) noexcept { - auto json_pointer = json_path_to_pointer_conversion(json_path); - if (json_pointer == "-1") { return INVALID_JSON_POINTER; } - return at_pointer(json_pointer); - } + }else{ + auto nested_result = element.at_path_with_wildcard(remaining_path); - simdjson_inline simdjson_result array::at(size_t index) noexcept { - size_t i = 0; - for (auto value : *this) { - if (i == index) { return value; } - i++; - } - return INDEX_OUT_OF_BOUNDS; - } + if(nested_result.error()){ + return nested_result.error(); + } + // Same logic as above. + std::vector nested_matches = std::move(nested_result).value_unsafe(); - } // namespace ondemand - } // namespace westmere + result.insert(result.end(), + std::make_move_iterator(nested_matches.begin()), + std::make_move_iterator(nested_matches.end())); + } + } + return result; + }else{ + // Specific index case in which we access the element at the given index + size_t idx=0; + + for(char c:key){ + if(c < '0' || c > '9'){ + return INVALID_JSON_POINTER; + } + idx = idx*10 + (c - '0'); + } + + auto element = at(idx); + + if(element.error()){ + return element.error(); + } + + if(remaining_path.empty()){ + result.push_back(std::move(element).value_unsafe()); + return result; + }else{ + return element.at_path_with_wildcard(remaining_path); + } + } +} + +simdjson_inline simdjson_result array::at(size_t index) noexcept { + 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::simdjson_result( - westmere::ondemand::array&& value - ) noexcept - : implementation_simdjson_result_base( - std::forward(value) - ) - { - } - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept - : implementation_simdjson_result_base(error) - { - } +simdjson_inline simdjson_result::simdjson_result( + westmere::ondemand::array &&value +) noexcept + : implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept + : implementation_simdjson_result_base(error) +{ +} - simdjson_inline simdjson_result simdjson_result::begin() noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() noexcept { - if (error()) { return error(); } - return first.end(); - } - simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); - } - simdjson_inline simdjson_result simdjson_result::is_empty() & noexcept { - if (error()) { return error(); } - return first.is_empty(); - } - simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } - simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { return error(); } - return first.at_path(json_path); - } - simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { - if (error()) { return error(); } - return first.raw_json(); - } +simdjson_inline simdjson_result simdjson_result::begin() noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() noexcept { + if (error()) { return error(); } + return first.end(); +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::is_empty() & noexcept { + if (error()) { return error(); } + return first.is_empty(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path(json_path); +} +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path_with_wildcard(json_path); +} +simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { + if (error()) { return error(); } + return first.raw_json(); +} } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H @@ -91782,74 +135129,661 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace ondemand { +namespace westmere { +namespace ondemand { - simdjson_inline array_iterator::array_iterator(const value_iterator& _iter) noexcept - : iter{ _iter } - {} +simdjson_inline array_iterator::array_iterator(const value_iterator &_iter) noexcept + : iter{_iter} +{} - simdjson_inline simdjson_result array_iterator::operator*() noexcept { - 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 { - 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 simdjson_result 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; +} - } // namespace ondemand - } // namespace westmere +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::simdjson_result( - westmere::ondemand::array_iterator&& value - ) noexcept - : westmere::implementation_simdjson_result_base(std::forward(value)) - { - first.iter.assert_is_valid(); - } - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : westmere::implementation_simdjson_result_base({}, error) - { - } - - simdjson_inline simdjson_result simdjson_result::operator*() noexcept { - if (error()) { return error(); } - return *first; - } - simdjson_inline bool simdjson_result::operator==(const simdjson_result& other) const noexcept { - if (!first.iter.is_valid()) { return !error(); } - return first == other.first; - } - simdjson_inline bool simdjson_result::operator!=(const simdjson_result& other) const noexcept { - if (!first.iter.is_valid()) { return error(); } - return first != other.first; - } - simdjson_inline simdjson_result& simdjson_result::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 simdjson_result::simdjson_result( + westmere::ondemand::array_iterator &&value +) noexcept + : westmere::implementation_simdjson_result_base(std::forward(value)) +{ + first.iter.assert_is_valid(); +} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : westmere::implementation_simdjson_result_base({}, error) +{ +} +simdjson_inline simdjson_result simdjson_result::operator*() noexcept { + if (error()) { return error(); } + return *first; +} +simdjson_inline bool simdjson_result::operator==(const simdjson_result &other) const noexcept { + if (!first.iter.is_valid()) { return !error(); } + return first == other.first; +} +simdjson_inline bool simdjson_result::operator!=(const simdjson_result &other) const noexcept { + if (!first.iter.is_valid()) { return error(); } + return first != other.first; +} +simdjson_inline simdjson_result &simdjson_result::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::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 */ + +namespace simdjson { +namespace westmere { +namespace ondemand { + +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 value::get_array() noexcept { + return array::start(iter); +} +simdjson_inline simdjson_result value::get_object() noexcept { + return object::start(iter); +} +simdjson_inline simdjson_result value::start_or_resume_object() noexcept { + if (iter.at_start()) { + return get_object(); + } else { + return object::resume(iter); + } +} + +simdjson_inline simdjson_result value::get_raw_json_string() noexcept { + return iter.get_raw_json_string(); +} +simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { + return iter.get_string(allow_replacement); +} +template +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 value::get_wobbly_string() noexcept { + return iter.get_wobbly_string(); +} +simdjson_inline simdjson_result value::get_double() noexcept { + return iter.get_double(); +} +simdjson_inline simdjson_result value::get_double_in_string() noexcept { + return iter.get_double_in_string(); +} +simdjson_inline simdjson_result value::get_uint64() noexcept { + return iter.get_uint64(); +} +simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { + return iter.get_uint64_in_string(); +} +simdjson_inline simdjson_result value::get_int64() noexcept { + return iter.get_int64(); +} +simdjson_inline simdjson_result value::get_int64_in_string() noexcept { + return iter.get_int64_in_string(); +} +simdjson_inline simdjson_result value::get_bool() noexcept { + return iter.get_bool(); +} +simdjson_inline simdjson_result value::is_null() noexcept { + return iter.is_null(); +} + +template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result 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); } +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(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(bool& out) noexcept { return get_bool().get(out); } + +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline value::operator T() noexcept(false) { + return get(); +} +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 value::begin() & noexcept { + return get_array().begin(); +} +simdjson_inline simdjson_result value::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result value::count_elements() & noexcept { + simdjson_result 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 value::count_fields() & noexcept { + simdjson_result answer; + auto a = get_object(); + answer = a.count_fields(); + iter.move_at_start(); + return answer; +} +simdjson_inline simdjson_result value::at(size_t index) noexcept { + auto a = get_array(); + return a.at(index); +} + +simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result value::find_field(const char *key) noexcept { + return start_or_resume_object().find_field(key); +} + +simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result value::find_field_unordered(const char *key) noexcept { + return start_or_resume_object().find_field_unordered(key); +} + +simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { + return start_or_resume_object()[key]; +} +simdjson_inline simdjson_result value::operator[](const char *key) noexcept { + return start_or_resume_object()[key]; +} + +simdjson_inline simdjson_result value::type() noexcept { + return iter.type(); +} + +simdjson_inline simdjson_result 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 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 value::is_integer() noexcept { + return iter.is_integer(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { + return iter.get_number_type(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { + return iter.get_number(); +} + +simdjson_inline std::string_view value::raw_json_token() noexcept { + return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); +} + +simdjson_inline simdjson_result 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 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::at_pointer(std::string_view json_pointer) noexcept { + 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::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; + } +} + +inline simdjson_result> value::at_path_with_wildcard(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_with_wildcard(json_path); + case json_type::object: + return (*this).get_object().at_path_with_wildcard(json_path); + default: + return INVALID_JSON_POINTER; + } +} + +} // namespace ondemand +} // namespace westmere +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + westmere::ondemand::value &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base(error) +{ +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + if (error()) { return error(); } + return {}; +} + +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) noexcept { + if (error()) { return error(); } + return first.find_field(key); +} + +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} + +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) noexcept { + if (error()) { return error(); } + return first[key]; +} + +simdjson_inline simdjson_result simdjson_result::get_array() noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} + +template<> simdjson_inline error_code simdjson_result::get(westmere::ondemand::value &out) noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} + +template simdjson_inline simdjson_result simdjson_result::get() noexcept { + if (error()) { return error(); } + return first.get(); +} +template simdjson_inline error_code simdjson_result::get(T &out) noexcept { + if (error()) { return error(); } + return first.get(out); +} + +template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { + if (error()) { return error(); } + return std::move(first); +} + +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline simdjson_result::operator T() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first.get(); +} +simdjson_inline simdjson_result::operator westmere::ondemand::array() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator westmere::ondemand::object() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator westmere::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { + if (error()) { return error(); } + return first.raw_json(); +} + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { + if (error()) { return error(); } + return first.current_depth(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer( + std::string_view json_pointer) noexcept { + if (error()) { + return error(); + } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + +inline simdjson_result> simdjson_result::at_path_with_wildcard( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path_with_wildcard(json_path); +} + +} // 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 @@ -91862,911 +135796,1055 @@ namespace simdjson { /* 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 */ namespace simdjson { - namespace westmere { - namespace ondemand { +namespace westmere { +namespace ondemand { - simdjson_inline document::document(ondemand::json_iterator&& _iter) noexcept - : iter{ std::forward(_iter) } - { - logger::log_start_value(iter, "document"); - } +simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept + : iter{std::forward(_iter)} +{ + logger::log_start_value(iter, "document"); +} - simdjson_inline document document::start(json_iterator&& iter) noexcept { - return document(std::forward(iter)); - } +simdjson_inline document document::start(json_iterator &&iter) noexcept { + return document(std::forward(iter)); +} - inline void document::rewind() noexcept { - iter.rewind(); - } +inline void document::rewind() noexcept { + iter.rewind(); +} - inline std::string document::to_debug_string() noexcept { - return iter.to_string(); - } +inline std::string document::to_debug_string() noexcept { + return iter.to_string(); +} - inline simdjson_result document::current_location() const noexcept { - return iter.current_location(); - } +inline simdjson_result document::current_location() const noexcept { + return iter.current_location(); +} - inline int32_t document::current_depth() const noexcept { - return iter.depth(); - } +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::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 document::start_or_resume_object() noexcept { - if (iter.at_root()) { - return get_object(); - } - else { - return object::resume(resume_value_iterator()); - } - } - simdjson_inline simdjson_result document::get_value() noexcept { - // Make sure we start any arrays or objects before returning, so that start_root_() - // gets called. +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 document::start_or_resume_object() noexcept { + if (iter.at_root()) { + return get_object(); + } else { + return object::resume(resume_value_iterator()); + } +} +simdjson_inline simdjson_result document::get_value() noexcept { + // Make sure we start any arrays or objects before returning, so that start_root_() + // 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. + // 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; } + 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 document::get_array() & noexcept { - auto value = get_root_value_iterator(); - return array::start_root(value); - } - simdjson_inline simdjson_result document::get_object() & noexcept { - auto value = get_root_value_iterator(); - return object::start_root(value); - } + // 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 document::get_array() & noexcept { + auto value = get_root_value_iterator(); + return array::start_root(value); +} +simdjson_inline simdjson_result 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. - */ +/** + * 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 document::get_uint64() noexcept { - return get_root_value_iterator().get_root_uint64(true); - } - simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { - return get_root_value_iterator().get_root_uint64_in_string(true); - } - simdjson_inline simdjson_result document::get_int64() noexcept { - return get_root_value_iterator().get_root_int64(true); - } - simdjson_inline simdjson_result document::get_int64_in_string() noexcept { - return get_root_value_iterator().get_root_int64_in_string(true); - } - simdjson_inline simdjson_result document::get_double() noexcept { - return get_root_value_iterator().get_root_double(true); - } - simdjson_inline simdjson_result document::get_double_in_string() noexcept { - return get_root_value_iterator().get_root_double_in_string(true); - } - simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { - return get_root_value_iterator().get_root_string(true, allow_replacement); - } - template - 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 document::get_wobbly_string() noexcept { - return get_root_value_iterator().get_root_wobbly_string(true); - } - simdjson_inline simdjson_result document::get_raw_json_string() noexcept { - return get_root_value_iterator().get_root_raw_json_string(true); - } - simdjson_inline simdjson_result document::get_bool() noexcept { - return get_root_value_iterator().get_root_bool(true); - } - simdjson_inline simdjson_result document::is_null() noexcept { - return get_root_value_iterator().is_root_null(true); - } +simdjson_inline simdjson_result document::get_uint64() noexcept { + return get_root_value_iterator().get_root_uint64(true); +} +simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { + return get_root_value_iterator().get_root_uint64_in_string(true); +} +simdjson_inline simdjson_result document::get_int64() noexcept { + return get_root_value_iterator().get_root_int64(true); +} +simdjson_inline simdjson_result document::get_int64_in_string() noexcept { + return get_root_value_iterator().get_root_int64_in_string(true); +} +simdjson_inline simdjson_result document::get_double() noexcept { + return get_root_value_iterator().get_root_double(true); +} +simdjson_inline simdjson_result document::get_double_in_string() noexcept { + return get_root_value_iterator().get_root_double_in_string(true); +} +simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { + return get_root_value_iterator().get_root_string(true, allow_replacement); +} +template +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 document::get_wobbly_string() noexcept { + return get_root_value_iterator().get_root_wobbly_string(true); +} +simdjson_inline simdjson_result document::get_raw_json_string() noexcept { + return get_root_value_iterator().get_root_raw_json_string(true); +} +simdjson_inline simdjson_result document::get_bool() noexcept { + return get_root_value_iterator().get_root_bool(true); +} +simdjson_inline simdjson_result document::is_null() noexcept { + return get_root_value_iterator().is_root_null(true); +} - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } - template<> simdjson_deprecated simdjson_inline simdjson_result 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); } +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(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(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_inline error_code document::get(T& out) & noexcept { - return get().get(out); - } - template simdjson_deprecated simdjson_inline error_code document::get(T& out) && noexcept { - return std::forward(*this).get().get(out); - } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_value(); } #if SIMDJSON_EXCEPTIONS - template - simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } - template - simdjson_inline document::operator T() & noexcept(false) { return get(); } - 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) { return get_string(false); } - simdjson_inline document::operator raw_json_string() noexcept(false) { 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(); } +template +simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } +template +simdjson_inline document::operator T() & noexcept(false) { return get(); } +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 document::count_elements() & noexcept { - auto a = get_array(); - simdjson_result 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 document::count_elements() & noexcept { + auto a = get_array(); + simdjson_result 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 document::count_fields() & noexcept { + auto a = get_object(); + simdjson_result 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 document::at(size_t index) & noexcept { + auto a = get_array(); + return a.at(index); +} +simdjson_inline simdjson_result document::begin() & noexcept { + return get_array().begin(); +} +simdjson_inline simdjson_result document::end() & noexcept { + return {}; +} + +simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field(const char *key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::find_field_unordered(const char *key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { + return start_or_resume_object()[key]; +} +simdjson_inline simdjson_result 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 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(starting_point), size_t(final_point - starting_point)); +} + +simdjson_inline simdjson_result document::type() noexcept { + return get_root_value_iterator().type(); +} + +simdjson_inline simdjson_result 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 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 document::is_integer() noexcept { + return get_root_value_iterator().is_root_integer(true); +} + +simdjson_inline simdjson_result document::get_number_type() noexcept { + return get_root_value_iterator().get_root_number_type(true); +} + +simdjson_inline simdjson_result document::get_number() noexcept { + return get_root_value_iterator().get_root_number(true); +} + + +simdjson_inline simdjson_result document::raw_json_token() noexcept { + auto _iter = get_root_value_iterator(); + return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); +} + +simdjson_inline simdjson_result 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 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; + } +} + +simdjson_inline simdjson_result> document::at_path_with_wildcard(std::string_view json_path) noexcept { + rewind(); // Rewind the document each time 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().at_path_with_wildcard(json_path); + case json_type::object: + return (*this).get_object().at_path_with_wildcard(json_path); + default: + return INVALID_JSON_POINTER; + } +} + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (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) { + // 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; } - simdjson_inline simdjson_result document::count_fields() & noexcept { - auto a = get_object(); - simdjson_result 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 document::at(size_t index) & noexcept { - auto a = get_array(); - return a.at(index); - } - simdjson_inline simdjson_result document::begin() & noexcept { - return get_array().begin(); - } - simdjson_inline simdjson_result document::end() & noexcept { - return {}; - } - - simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { - return start_or_resume_object().find_field(key); - } - simdjson_inline simdjson_result document::find_field(const char* key) & noexcept { - return start_or_resume_object().find_field(key); - } - simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { - return start_or_resume_object().find_field_unordered(key); - } - simdjson_inline simdjson_result document::find_field_unordered(const char* key) & noexcept { - return start_or_resume_object().find_field_unordered(key); - } - simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { - return start_or_resume_object()[key]; - } - simdjson_inline simdjson_result document::operator[](const char* key) & noexcept { - return start_or_resume_object()[key]; - } - - simdjson_inline error_code document::consume() noexcept { - auto error = iter.skip_child(0); - if (error) { iter.abandon(); } - return error; - } - - simdjson_inline simdjson_result 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(starting_point), size_t(final_point - starting_point)); - } - - simdjson_inline simdjson_result document::type() noexcept { - return get_root_value_iterator().type(); - } - - simdjson_inline simdjson_result document::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 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 document::is_integer() noexcept { - return get_root_value_iterator().is_root_integer(true); - } - - simdjson_inline simdjson_result document::get_number_type() noexcept { - return get_root_value_iterator().get_root_number_type(true); - } - - simdjson_inline simdjson_result document::get_number() noexcept { - return get_root_value_iterator().get_root_number(true); - } - - - simdjson_inline simdjson_result document::raw_json_token() noexcept { - auto _iter = get_root_value_iterator(); - return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); - } - - simdjson_inline simdjson_result 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 document::at_path(std::string_view json_path) noexcept { - rewind(); // Rewind the document each time at_pointer is called - if (json_path.empty()) { - return this->get_value(); - } - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) { - case json_type::array: - return (*this).get_array().at_path(json_path); - case json_type::object: - return (*this).get_object().at_path(json_path); - default: - return INVALID_JSON_POINTER; - } - } - - } // namespace ondemand - } // namespace westmere -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result( - westmere::ondemand::document&& value - ) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) - { - } - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept : - implementation_simdjson_result_base( - error - ) - { - } - simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); - } - simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); - } - simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; - } - simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() & noexcept { - return {}; - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char* key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](const char* key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::find_field(const char* key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { - if (error()) { return error(); } - return first.get_array(); - } - simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { - if (error()) { return error(); } - return first.get_object(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); - } - simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); - } - simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); - } - template - simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(receiver, allow_replacement); - } - simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); - } - simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); - } - simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); - } - simdjson_inline simdjson_result simdjson_result::get_value() noexcept { - if (error()) { return error(); } - return first.get_value(); - } - simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); - } - - template - simdjson_inline simdjson_result simdjson_result::get() & noexcept { - if (error()) { return error(); } - return first.get(); - } - template - simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { - if (error()) { return error(); } - return std::forward(first).get(); - } - template - simdjson_inline error_code simdjson_result::get(T& out) & noexcept { - if (error()) { return error(); } - return first.get(out); - } - template - simdjson_inline error_code simdjson_result::get(T& out) && noexcept { - if (error()) { return error(); } - return std::forward(first).get(out); - } - - template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; - template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { - if (error()) { return error(); } - return std::forward(first); - } - template<> simdjson_inline error_code simdjson_result::get(westmere::ondemand::document& out) & noexcept = delete; - template<> simdjson_inline error_code simdjson_result::get(westmere::ondemand::document& out) && noexcept { - if (error()) { return error(); } - out = std::forward(first); - return SUCCESS; - } - - simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); - } - - simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); - } - - simdjson_inline simdjson_result simdjson_result::is_string() noexcept { - if (error()) { return error(); } - return first.is_string(); - } - - simdjson_inline bool simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); - } - - simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); - } - - simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); - } - - simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); - } - - -#if SIMDJSON_EXCEPTIONS - template ::value == false>::type> - simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator westmere::ondemand::array() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator westmere::ondemand::object() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator westmere::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator westmere::ondemand::value() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } -#endif - - - simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); - } - - simdjson_inline bool simdjson_result::at_end() const noexcept { - if (error()) { return error(); } - return first.at_end(); - } - - - simdjson_inline int32_t simdjson_result::current_depth() const noexcept { - if (error()) { return error(); } - return first.current_depth(); - } - - simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); - } - - simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } - - simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { return error(); } - return first.at_path(json_path); - } - -} // namespace simdjson - - -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 document_reference::get_array() & noexcept { return doc->get_array(); } - simdjson_inline simdjson_result 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 document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } - simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } - simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } - simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } - simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } - simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } - simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } - template - 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 document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } - simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } - simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } - simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } - simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } -#if SIMDJSON_EXCEPTIONS - template - simdjson_inline document_reference::operator T() noexcept(false) { return get(); } - 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 document_reference::count_elements() & noexcept { return doc->count_elements(); } - simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } - simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } - simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } - simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } - simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } - simdjson_inline simdjson_result document_reference::find_field(const char* key) & noexcept { return doc->find_field(key); } - simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } - simdjson_inline simdjson_result document_reference::operator[](const char* key) & noexcept { return (*doc)[key]; } - simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } - simdjson_inline simdjson_result document_reference::find_field_unordered(const char* key) & noexcept { return doc->find_field_unordered(key); } - simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } - simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } - simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } - simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } - simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } - simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } - simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } - simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } - simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } - simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } - simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } - simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } - simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json(); } - simdjson_inline document_reference::operator document& () const noexcept { return *doc; } - - } // namespace ondemand - } // namespace westmere -} // namespace simdjson - - - -namespace simdjson { - simdjson_inline simdjson_result::simdjson_result(westmere::ondemand::document_reference value, error_code error) - noexcept : implementation_simdjson_result_base(std::forward(value), error) {} - - - simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); - } - simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); - } - simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; - } - simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() & noexcept { - return {}; - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char* key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](const char* key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::find_field(const char* key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { - if (error()) { return error(); } - return first.get_array(); - } - simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { - if (error()) { return error(); } - return first.get_object(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); - } - simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); - } - simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); - } - template - simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(receiver, allow_replacement); - } - simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); - } - simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); - } - simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); - } - simdjson_inline simdjson_result simdjson_result::get_value() noexcept { - if (error()) { return error(); } - return first.get_value(); - } - simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); - } - simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); - } - simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); - } - simdjson_inline simdjson_result simdjson_result::is_string() noexcept { - if (error()) { return error(); } - return first.is_string(); - } - simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); - } - simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); - } - simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); - } - simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); - } -#if SIMDJSON_EXCEPTIONS - template ::value == false>::type> - simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator westmere::ondemand::array() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator westmere::ondemand::object() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator westmere::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator westmere::ondemand::value() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } -#endif - - simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); - } - - simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); - } - - simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } - - simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { - return error(); + } 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 first.at_path(json_path); + } } + }; + return SUCCESS; +} + +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +} // namespace ondemand +} // namespace westmere +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + westmere::ondemand::document &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base( + error + ) +{ +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { + if (error()) { return error(); } + return first.get_value(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} + +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} + +template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; +template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first); +} +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(westmere::ondemand::document &out) & noexcept = delete; +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(westmere::ondemand::document &out) && noexcept { + if (error()) { return error(); } + out = std::forward(first); + return SUCCESS; +} + +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} + +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} + +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} + +simdjson_inline bool simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} + +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} + +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} + +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} + + +#if SIMDJSON_EXCEPTIONS +template ::value == false>::type> +simdjson_inline simdjson_result::operator T() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator westmere::ondemand::array() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator westmere::ondemand::object() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator westmere::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator westmere::ondemand::value() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline bool simdjson_result::at_end() const noexcept { + if (error()) { return error(); } + return first.at_end(); +} + + +simdjson_inline int32_t simdjson_result::current_depth() const noexcept { + if (error()) { return error(); } + return first.current_depth(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path(json_path); +} + +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path_with_wildcard(json_path); +} + +#if SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(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 document_reference::get_array() & noexcept { return doc->get_array(); } +simdjson_inline simdjson_result 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 document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } +simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } +simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } +template +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 document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } +simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } +simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } +simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } +simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline document_reference::operator T() noexcept(false) { return get(); } +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 document_reference::count_elements() & noexcept { return doc->count_elements(); } +simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } +simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } +simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } +simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } +simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } +simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } +simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } +simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } +simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } +simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } +simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } +simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } +simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } +simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } +simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } +simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } +simdjson_inline simdjson_result> document_reference::at_path_with_wildcard(std::string_view json_path) noexcept { return doc->at_path_with_wildcard(json_path); } +simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} +simdjson_inline document_reference::operator document&() const noexcept { return *doc; } +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code document_reference::extract_into(T& out) & noexcept { + return doc->extract_into(out); +} +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION +} // namespace ondemand +} // namespace westmere +} // namespace simdjson + + + +namespace simdjson { +simdjson_inline simdjson_result::simdjson_result(westmere::ondemand::document_reference value, error_code error) + noexcept : implementation_simdjson_result_base(std::forward(value), error) {} + + +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { + if (error()) { return error(); } + return first.get_value(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} +template <> +simdjson_warn_unused simdjson_inline error_code simdjson_result::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::get(westmere::ondemand::document_reference &out) && noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline simdjson_result::operator T() noexcept(false) { + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + if (error()) { throw simdjson_error(error()); } + return first.get(); +} +simdjson_inline simdjson_result::operator westmere::ondemand::array() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator westmere::ondemand::object() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator westmere::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator westmere::ondemand::value() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path_with_wildcard(json_path); +} +#if SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(out); +} +#endif // SIMDJSON_STATIC_REFLECTION } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H @@ -92787,423 +136865,432 @@ namespace simdjson { #include namespace simdjson { - namespace westmere { - namespace ondemand { +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 lock(locking_mutex); - cond_var.wait(lock, [this] {return has_work == false; }); - } +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 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 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 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 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::start_thread() { + std::unique_lock 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 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 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::stop_thread() { + std::unique_lock 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 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(); - } +inline void stage1_worker::run(document_stream * ds, parser * stage1, size_t next_batch_start) { + std::unique_lock 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 - ) 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 }, - error{ SUCCESS } +simdjson_inline document_stream::document_stream( + ondemand::parser &_parser, + const uint8_t *_buf, + size_t _len, + size_t _batch_size, + bool _allow_comma_separated +) 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}, + 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 - , use_thread(_parser.threaded) // we need to make a copy because _parser.threaded can change + if(worker.get() == nullptr) { + error = MEMALLOC; + } #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 }, - allow_comma_separated{ false }, - error{ UNINITIALIZED } -#ifdef SIMDJSON_THREADS_ENABLED - , use_thread(false) -#endif - { - } +simdjson_inline document_stream::document_stream() noexcept + : parser{nullptr}, + buf{nullptr}, + len{0}, + batch_size{0}, + allow_comma_separated{false}, + 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::~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::size_in_bytes() const noexcept { + return len; +} - inline size_t document_stream::truncated_bytes() const noexcept { - 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::truncated_bytes() const noexcept { + 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() 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 document_stream::iterator::iterator(document_stream* _stream, bool is_end) noexcept + : stream{_stream}, finished{is_end} { +} - simdjson_inline simdjson_result document_stream::iterator::operator*() noexcept { - //if(stream->error) { return stream->error; } - return simdjson_result(stream->doc, stream->error); - } +simdjson_inline simdjson_result document_stream::iterator::operator*() noexcept { + return simdjson_result(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 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; - } +simdjson_inline bool document_stream::iterator::at_end() const noexcept { + return 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); - } +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->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; } - doc_index = batch_start; - doc = document(json_iterator(&buf[batch_start], parser)); - doc.iter._streaming = true; +simdjson_inline bool document_stream::iterator::operator==(const document_stream::iterator &other) const noexcept { + return finished == other.finished; +} -#ifdef SIMDJSON_THREADS_ENABLED - if (use_thread && next_batch_start() < len) { - // Kick off the first thread on next batch if needed - error = stage1_thread_parser.allocate(batch_size); - if (error) { return; } - worker->start_thread(); - start_stage1_thread(); - if (error) { return; } - } -#endif // SIMDJSON_THREADS_ENABLED - } +simdjson_inline document_stream::iterator document_stream::begin() noexcept { + start(); + // If there are no documents, we're finished. + return iterator(this, error == EMPTY); +} - 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]; +simdjson_inline document_stream::iterator document_stream::end() noexcept { + return iterator(this, true); +} - // Check if at end of structural indexes (i.e. at end of batch) - if (cur_struct_index >= static_cast(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. - */ +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; } + doc_index = batch_start; + doc = document(json_iterator(&buf[batch_start], parser)); + doc.iter._streaming = true; - if (error) { continue; } // If the error was EMPTY, we may want to load another batch. - doc_index = batch_start; - } - } - } + #ifdef SIMDJSON_THREADS_ENABLED + if (use_thread && next_batch_start() < len) { + // Kick off the first thread on next batch if needed + 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_document() noexcept { - // 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) { doc.iter.consume_character(','); } - // 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 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]; - inline size_t document_stream::next_batch_start() const noexcept { - return batch_start + parser->implementation->structural_indexes[parser->implementation->n_structural_indexes]; - } + // Check if at end of structural indexes (i.e. at end of batch) + if(cur_struct_index >= static_cast(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. + */ - 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; - if (remaining <= batch_size) { - return p.implementation->stage1(&buf[_batch_start], remaining, stage1_mode::streaming_final); - } - else { - return p.implementation->stage1(&buf[_batch_start], batch_size, stage1_mode::streaming_partial); - } - } + if (error) { continue; } // If the error was EMPTY, we may want to load another batch. + doc_index = batch_start; + } + } +} - simdjson_inline size_t document_stream::iterator::current_index() const noexcept { - return stream->doc_index; - } +inline void document_stream::next_document() noexcept { + // 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(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(); +} - simdjson_inline std::string_view document_stream::iterator::source() const noexcept { - auto depth = stream->doc.iter.depth(); - auto cur_struct_index = stream->doc.iter._root - stream->parser->implementation->structural_indexes.get(); +inline size_t document_stream::next_batch_start() const noexcept { + return batch_start + parser->implementation->structural_indexes[parser->implementation->n_structural_indexes]; +} - // If at root, process the first token to determine if scalar value - if (stream->doc.iter.at_root()) { - 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 - // TODO: We could remove trailing whitespaces - // This returns a string spanning from start of value to the beginning of the next document (excluded) - { - auto next_index = 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(stream->buf) + current_index(); - while (svlen > 1 && (std::isspace(start[svlen - 1]) || start[svlen - 1] == '\0')) { - svlen--; - } - return std::string_view(start, svlen); - } - } - cur_struct_index++; - } +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; + if (remaining <= batch_size) { + return p.implementation->stage1(&buf[_batch_start], remaining, stage1_mode::streaming_final); + } else { + return p.implementation->stage1(&buf[_batch_start], batch_size, stage1_mode::streaming_partial); + } +} - while (cur_struct_index <= static_cast(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++; - } +simdjson_inline size_t document_stream::iterator::current_index() const noexcept { + return stream->doc_index; +} - return std::string_view(reinterpret_cast(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[cur_struct_index] - current_index() + stream->batch_start + 1);; - } +simdjson_inline std::string_view document_stream::iterator::source() const noexcept { + auto depth = stream->doc.iter.depth(); + auto cur_struct_index = stream->doc.iter._root - stream->parser->implementation->structural_indexes.get(); - inline error_code document_stream::iterator::error() const noexcept { - return stream->error; - } + // If at root, process the first token to determine if scalar value + if (stream->doc.iter.at_root()) { + 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 + // TODO: We could remove trailing whitespaces + // This returns a string spanning from start of value to the beginning of the next document (excluded) + { + auto next_index = 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(stream->buf) + current_index(); + while(svlen > 1 && (std::isspace(start[svlen-1]) || start[svlen-1] == '\0')) { + svlen--; + } + return std::string_view(start, svlen); + } + } + cur_struct_index++; + } + + while (cur_struct_index <= static_cast(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(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; } +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(); - } - } + // 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(); +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); - } + worker->run(this, & this->stage1_thread_parser, _next_batch_start); +} #endif // SIMDJSON_THREADS_ENABLED - } // namespace ondemand - } // namespace westmere +} // namespace ondemand +} // namespace westmere } // namespace simdjson namespace simdjson { - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept : - implementation_simdjson_result_base(error) - { - } - simdjson_inline simdjson_result::simdjson_result( - westmere::ondemand::document_stream&& value - ) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) - { - } +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base(error) +{ +} +simdjson_inline simdjson_result::simdjson_result( + westmere::ondemand::document_stream &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} } @@ -93222,120 +137309,120 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace ondemand { +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() {} +// clang 6 does not think the default constructor can be noexcept, so we make it explicit +simdjson_inline field::field() noexcept : std::pair() {} - simdjson_inline field::field(raw_json_string key, ondemand::value&& value) noexcept - : std::pair(key, std::forward(value)) - { - } +simdjson_inline field::field(raw_json_string key, ondemand::value &&value) noexcept + : std::pair(key, std::forward(value)) +{ +} - simdjson_inline simdjson_result 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::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::start(const value_iterator& parent_iter, raw_json_string key) noexcept { - return field(key, parent_iter.child()); - } +simdjson_inline simdjson_result 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 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 answer = first.unescape(second.iter.json_iter(), allow_replacement); - first.consume(); - return answer; - } +simdjson_inline simdjson_warn_unused simdjson_result 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 answer = first.unescape(second.iter.json_iter(), allow_replacement); + first.consume(); + return answer; +} - template - 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)); - receiver = key; - return SUCCESS; - } +template +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) ); + 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 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(first.buf - 1), second.iter._json_iter->token.peek(-1) - first.buf + 1); - } +simdjson_inline std::string_view field::key_raw_json_token() const noexcept { + SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us. + return std::string_view(reinterpret_cast(first.buf-1), second.iter._json_iter->token.peek(-1) - first.buf + 1); +} - simdjson_inline 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(first.buf), end_quote - first.buf); - } +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(first.buf), end_quote - first.buf); +} - simdjson_inline value& field::value() & noexcept { - return second; - } +simdjson_inline value &field::value() & noexcept { + return second; +} - simdjson_inline value field::value() && noexcept { - return std::forward(*this).second; - } +simdjson_inline value field::value() && noexcept { + return std::forward(*this).second; +} - } // namespace ondemand - } // namespace westmere +} // namespace ondemand +} // namespace westmere } // namespace simdjson namespace simdjson { - simdjson_inline simdjson_result::simdjson_result( - westmere::ondemand::field&& value - ) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) - { - } - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept : - implementation_simdjson_result_base(error) - { - } +simdjson_inline simdjson_result::simdjson_result( + westmere::ondemand::field &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base(error) +{ +} - simdjson_inline simdjson_result simdjson_result::key() noexcept { - if (error()) { return error(); } - return first.key(); - } +simdjson_inline simdjson_result simdjson_result::key() noexcept { + if (error()) { return error(); } + return first.key(); +} - simdjson_inline simdjson_result simdjson_result::key_raw_json_token() noexcept { - if (error()) { return error(); } - return first.key_raw_json_token(); - } +simdjson_inline simdjson_result simdjson_result::key_raw_json_token() noexcept { + if (error()) { return error(); } + return first.key_raw_json_token(); +} - simdjson_inline simdjson_result simdjson_result::escaped_key() noexcept { - if (error()) { return error(); } - return first.escaped_key(); - } +simdjson_inline simdjson_result simdjson_result::escaped_key() noexcept { + if (error()) { return error(); } + return first.escaped_key(); +} - simdjson_inline simdjson_result simdjson_result::unescaped_key(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.unescaped_key(allow_replacement); - } +simdjson_inline simdjson_result simdjson_result::unescaped_key(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.unescaped_key(allow_replacement); +} - template - simdjson_inline error_code simdjson_result::unescaped_key(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.unescaped_key(receiver, allow_replacement); - } +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::unescaped_key(string_type &receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.unescaped_key(receiver, allow_replacement); +} - simdjson_inline simdjson_result simdjson_result::value() noexcept { - if (error()) { return error(); } - return std::move(first.value()); - } +simdjson_inline simdjson_result simdjson_result::value() noexcept { + if (error()) { return error(); } + return std::move(first.value()); +} } // namespace simdjson @@ -93358,432 +137445,440 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace ondemand { +namespace westmere { +namespace ondemand { - simdjson_inline json_iterator::json_iterator(json_iterator&& other) noexcept - : token(std::forward(other.token)), - parser{ other.parser }, - _string_buf_loc{ other._string_buf_loc }, - error{ other.error }, - _depth{ other._depth }, - _root{ other._root }, - _streaming{ other._streaming } - { - 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; - other.parser = nullptr; - return *this; - } +simdjson_inline json_iterator::json_iterator(json_iterator &&other) noexcept + : token(std::forward(other.token)), + parser{other.parser}, + _string_buf_loc{other._string_buf_loc}, + error{other.error}, + _depth{other._depth}, + _root{other._root}, + _streaming{other._streaming} +{ + 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; + 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 } +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(); +{ + logger::log_headers(); #if SIMDJSON_CHECK_EOF - assert_more_tokens(); + 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 } +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} - { - logger::log_headers(); +{ + logger::log_headers(); #if SIMDJSON_CHECK_EOF - assert_more_tokens(); + 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 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; - } +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 +// 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; } + // 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"); } + // 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 '"': - if (*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; - } + break; + case '"': + if(*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; - } - } + // 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(TAPE_ERROR, "not enough close braces"); - } + return report_error(TAPE_ERROR, "not enough close braces"); +} - SIMDJSON_POP_DISABLE_WARNINGS +SIMDJSON_POP_DISABLE_WARNINGS - simdjson_inline bool json_iterator::at_root() const noexcept { - return position() == root_position(); - } +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::is_single_token() const noexcept { + return parser->implementation->n_structural_indexes == 1; +} - simdjson_inline bool json_iterator::streaming() const noexcept { - return _streaming; - } +simdjson_inline bool json_iterator::streaming() const noexcept { + return _streaming; +} - simdjson_inline token_position json_iterator::root_position() const noexcept { - return _root; - } +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_document_depth() const noexcept { + SIMDJSON_ASSUME( _depth == 1 ); +} - simdjson_inline void json_iterator::assert_at_root() 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); + // 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_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 { +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]); + 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]; - } +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(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 std::string json_iterator::to_string() const noexcept { + if( !is_alive() ) { return "dead json_iterator instance"; } + const char * current_structural = reinterpret_cast(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 json_iterator::current_location() const noexcept { - if (!is_alive()) { // Unrecoverable error - if (!at_root()) { - return reinterpret_cast(token.peek(-1)); - } - else { - return reinterpret_cast(token.peek()); - } - } - if (at_end()) { - return OUT_OF_BOUNDS; - } - return reinterpret_cast(token.peek()); - } +inline simdjson_result json_iterator::current_location() const noexcept { + if (!is_alive()) { // Unrecoverable error + if (!at_root()) { + return reinterpret_cast(token.peek(-1)); + } else { + return reinterpret_cast(token.peek()); + } + } + if (at_end()) { + return OUT_OF_BOUNDS; + } + return reinterpret_cast(token.peek()); +} - simdjson_inline bool json_iterator::is_alive() const noexcept { - return parser; - } +simdjson_inline bool json_iterator::is_alive() const noexcept { + return parser; +} - simdjson_inline void json_iterator::abandon() noexcept { - parser = nullptr; - _depth = 0; - } +simdjson_inline void json_iterator::abandon() noexcept { + parser = nullptr; + _depth = 0; +} - simdjson_inline const uint8_t* json_iterator::return_current_and_advance() noexcept { +simdjson_inline const uint8_t *json_iterator::return_current_and_advance() noexcept { #if SIMDJSON_CHECK_EOF - assert_more_tokens(); + assert_more_tokens(); #endif // SIMDJSON_CHECK_EOF - return token.return_current_and_advance(); - } + 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::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 { +simdjson_inline const uint8_t *json_iterator::peek(int32_t delta) const noexcept { #if SIMDJSON_CHECK_EOF - assert_more_tokens(delta + 1); + assert_more_tokens(delta+1); #endif // SIMDJSON_CHECK_EOF - return token.peek(delta); - } + return token.peek(delta); +} - simdjson_inline uint32_t json_iterator::peek_length(int32_t delta) const noexcept { +simdjson_inline uint32_t json_iterator::peek_length(int32_t delta) const noexcept { #if SIMDJSON_CHECK_EOF - assert_more_tokens(delta + 1); + assert_more_tokens(delta+1); #endif // #if SIMDJSON_CHECK_EOF - return token.peek_length(delta); - } + 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 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 { +simdjson_inline uint32_t json_iterator::peek_length(token_position position) const noexcept { #if SIMDJSON_CHECK_EOF - assert_valid_position(position); + 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 { + 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); + assert_valid_position(position); #endif // SIMDJSON_CHECK_EOF - return token.peek_root_length(position); - } + 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 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::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 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 depth_t json_iterator::depth() const noexcept { + return _depth; +} - simdjson_inline uint8_t*& json_iterator::string_buf_loc() noexcept { - return _string_buf_loc; - } +simdjson_inline uint8_t *&json_iterator::string_buf_loc() noexcept { + return _string_buf_loc; +} - 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_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 token_position json_iterator::position() const noexcept { + return token.position(); +} - simdjson_inline simdjson_result json_iterator::unescape(raw_json_string in, bool allow_replacement) noexcept { +simdjson_inline simdjson_result 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); - SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc)); - return result; + 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); + return parser->unescape(in, _string_buf_loc, allow_replacement); #endif - } +} - simdjson_inline simdjson_result json_iterator::unescape_wobbly(raw_json_string in) noexcept { +simdjson_inline simdjson_result json_iterator::unescape_wobbly(raw_json_string in) noexcept { #if SIMDJSON_DEVELOPMENT_CHECKS - auto result = parser->unescape_wobbly(in, _string_buf_loc); - SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc)); - return result; + 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); + 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); +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]); + 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; - } + token.set_position(position); + _depth = child_depth; +} - simdjson_inline error_code json_iterator::consume_character(char c) noexcept { - if (*peek() == c) { - return_current_and_advance(); - return SUCCESS; - } - return TAPE_ERROR; - } +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 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; } - } +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_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 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; - } +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 ondemand +} // namespace westmere } // namespace simdjson namespace simdjson { - simdjson_inline simdjson_result::simdjson_result(westmere::ondemand::json_iterator&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} +simdjson_inline simdjson_result::simdjson_result(westmere::ondemand::json_iterator &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(error) {} } // namespace simdjson @@ -93801,109 +137896,109 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace ondemand { +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; - default: SIMDJSON_UNREACHABLE(); - } - return out; - } +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; + default: SIMDJSON_UNREACHABLE(); + } + return out; +} #if SIMDJSON_EXCEPTIONS - inline std::ostream& operator<<(std::ostream& out, simdjson_result& type) noexcept(false) { - return out << type.value(); - } +inline std::ostream& operator<<(std::ostream& out, simdjson_result &type) noexcept(false) { + return out << type.value(); +} #endif - simdjson_inline number_type number::get_number_type() const noexcept { - return type; - } +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 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 uint64_t number::get_uint64() const noexcept { + return payload.unsigned_integer; +} - simdjson_inline number::operator uint64_t() const noexcept { - return get_uint64(); - } +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 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 int64_t number::get_int64() const noexcept { + return payload.signed_integer; +} - simdjson_inline number::operator int64_t() const noexcept { - return get_int64(); - } +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 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 double number::get_double() const noexcept { + return payload.floating_point_number; +} - simdjson_inline number::operator double() const noexcept { - return get_double(); - } +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 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_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_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::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; - } +simdjson_inline void number::skip_double() noexcept { + type = number_type::floating_point_number; +} - } // namespace ondemand - } // namespace westmere +} // namespace ondemand +} // namespace westmere } // namespace simdjson namespace simdjson { - simdjson_inline simdjson_result::simdjson_result(westmere::ondemand::json_type&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} +simdjson_inline simdjson_result::simdjson_result(westmere::ondemand::json_type &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(error) {} } // namespace simdjson @@ -93925,216 +138020,214 @@ namespace simdjson { #include namespace simdjson { - namespace westmere { - namespace ondemand { - namespace logger { +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. +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 ' '; - } - } +// Helper to turn unprintable or newline characters into spaces +static inline char printable_char(char c) { + if (c >= 0x20) { + return c; + } else { + return ' '; + } +} - template - 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_s); - if (size <= 0) return std::string(); - std::unique_ptr 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); - } +template +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_s); + if (size <= 0) return std::string(); + std::unique_ptr 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 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 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(); - } +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_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_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_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_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_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_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_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_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_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_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"); +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); - } - } - } + 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 - 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)...); - } +template +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)...); +} - template - 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)...); - 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); - } - } - } +template +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)...); + 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 logger +} // namespace ondemand +} // namespace westmere } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_LOGGER_INL_H @@ -94152,269 +138245,369 @@ namespace simdjson { /* 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): #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 */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace ondemand { +namespace westmere { +namespace ondemand { - simdjson_inline simdjson_result 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(key.size()), key.data()); - return NO_SUCH_FIELD; - } - return value(iter.child()); - } - simdjson_inline simdjson_result 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(key.size()), key.data()); - return NO_SUCH_FIELD; - } - return value(iter.child()); - } - simdjson_inline simdjson_result object::operator[](const std::string_view key) & noexcept { - return find_field_unordered(key); - } - simdjson_inline simdjson_result object::operator[](const std::string_view key) && noexcept { - return std::forward(*this).find_field_unordered(key); - } - simdjson_inline simdjson_result object::find_field(const std::string_view key) & noexcept { - 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(key.size()), key.data()); - return NO_SUCH_FIELD; - } - return value(iter.child()); - } - simdjson_inline simdjson_result object::find_field(const std::string_view key) && noexcept { - 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(key.size()), key.data()); - return NO_SUCH_FIELD; - } - return value(iter.child()); - } +simdjson_inline simdjson_result 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} +simdjson_inline simdjson_result 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} +simdjson_inline simdjson_result object::operator[](const std::string_view key) & noexcept { + return find_field_unordered(key); +} +simdjson_inline simdjson_result object::operator[](const std::string_view key) && noexcept { + return std::forward(*this).find_field_unordered(key); +} +simdjson_inline simdjson_result object::find_field(const std::string_view key) & noexcept { + 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} +simdjson_inline simdjson_result object::find_field(const std::string_view key) && noexcept { + 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} - simdjson_inline simdjson_result object::start(value_iterator& iter) noexcept { - SIMDJSON_TRY(iter.start_object().error()); - return object(iter); - } - simdjson_inline simdjson_result object::start_root(value_iterator& iter) noexcept { - SIMDJSON_TRY(iter.start_root_object().error()); - return object(iter); - } - 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 object::start(value_iterator &iter) noexcept { + SIMDJSON_TRY( iter.start_object().error() ); + return object(iter); +} +simdjson_inline simdjson_result 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 object::raw_json() noexcept { - 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(starting_point), size_t(final_point - starting_point)); - } +simdjson_inline simdjson_result object::raw_json() noexcept { + 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(starting_point), size_t(final_point - starting_point)); +} - simdjson_inline simdjson_result object::started(value_iterator& iter) noexcept { - SIMDJSON_TRY(iter.started_object().error()); - return object(iter); - } +simdjson_inline simdjson_result 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::resume(const value_iterator &iter) noexcept { + return iter; +} - simdjson_inline object::object(const value_iterator& _iter) noexcept - : iter{ _iter } - { - } +simdjson_inline object::object(const value_iterator &_iter) noexcept + : iter{_iter} +{ +} - simdjson_inline simdjson_result object::begin() noexcept { +simdjson_inline simdjson_result object::begin() noexcept { #if SIMDJSON_DEVELOPMENT_CHECKS - if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } + if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } #endif - return object_iterator(iter); - } - simdjson_inline simdjson_result object::end() noexcept { - return object_iterator(iter); - } + return object_iterator(iter); +} +simdjson_inline simdjson_result object::end() noexcept { + return object_iterator(iter); +} - inline simdjson_result object::at_pointer(std::string_view json_pointer) noexcept { - if (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 child; +inline simdjson_result object::at_pointer(std::string_view json_pointer) noexcept { + if (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 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; + // 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 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); +} + +inline simdjson_result> object::at_path_with_wildcard(std::string_view json_path) noexcept { + std::vector result; + + 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; + // 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()) { + result.push_back(std::move(val)); + } else { + auto nested_result = val.at_path_with_wildcard(remaining_path); + + if (nested_result.error()) { + return nested_result.error(); + } + // Extract and append all nested matches to our result + std::vector nested_vec; + SIMDJSON_TRY(std::move(nested_result).get(nested_vec)); + + result.insert(result.end(), + std::make_move_iterator(nested_vec.begin()), + std::make_move_iterator(nested_vec.end())); + } + } + return result; + } else { + value val; + SIMDJSON_TRY(find_field(key).get(val)); + + if (remaining_path.empty()) { + result.push_back(std::move(val)); + return result; + } else { + return val.at_path_with_wildcard(remaining_path); + } + } +} + +simdjson_inline simdjson_result 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 object::is_empty() & noexcept { + 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 object::reset() & noexcept { + return iter.reset_object(); +} + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (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) { + // 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:])); + } + } + } + }; - inline simdjson_result object::at_path(std::string_view json_path) noexcept { - auto json_pointer = json_path_to_pointer_conversion(json_path); - if (json_pointer == "-1") { - return INVALID_JSON_POINTER; - } - return at_pointer(json_pointer); - } + return SUCCESS; +} - simdjson_inline simdjson_result 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; - } +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION - simdjson_inline simdjson_result object::is_empty() & noexcept { - 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 object::reset() & noexcept { - return iter.reset_object(); - } - - } // namespace ondemand - } // namespace westmere +} // namespace ondemand +} // namespace westmere } // namespace simdjson namespace simdjson { - simdjson_inline simdjson_result::simdjson_result(westmere::ondemand::object&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} +simdjson_inline simdjson_result::simdjson_result(westmere::ondemand::object &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(error) {} - simdjson_inline simdjson_result simdjson_result::begin() noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() noexcept { - if (error()) { return error(); } - return first.end(); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) && noexcept { - if (error()) { return error(); } - return std::forward(first).find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) && noexcept { - if (error()) { return error(); } - return std::forward(first)[key]; - } - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) && noexcept { - if (error()) { return error(); } - return std::forward(first).find_field(key); - } +simdjson_inline simdjson_result simdjson_result::begin() noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() noexcept { + if (error()) { return error(); } + return first.end(); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) && noexcept { + if (error()) { return error(); } + return std::forward(first).find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) && noexcept { + if (error()) { return error(); } + return std::forward(first)[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) && noexcept { + if (error()) { return error(); } + return std::forward(first).find_field(key); +} - simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} - simdjson_inline simdjson_result simdjson_result::at_path( - std::string_view json_path) noexcept { - if (error()) { - return error(); - } - return first.at_path(json_path); - } +simdjson_inline simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} - inline simdjson_result simdjson_result::reset() noexcept { - if (error()) { return error(); } - return first.reset(); - } +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path_with_wildcard(json_path); +} - inline simdjson_result simdjson_result::is_empty() noexcept { - if (error()) { return error(); } - return first.is_empty(); - } +inline simdjson_result simdjson_result::reset() noexcept { + if (error()) { return error(); } + return first.reset(); +} - simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); - } +inline simdjson_result simdjson_result::is_empty() noexcept { + if (error()) { return error(); } + return first.is_empty(); +} + +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { + if (error()) { return error(); } + return first.raw_json(); +} - simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { - if (error()) { return error(); } - return first.raw_json(); - } } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H @@ -94432,129 +138625,139 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace ondemand { +namespace westmere { +namespace ondemand { - // - // object_iterator - // +// +// object_iterator +// - simdjson_inline object_iterator::object_iterator(const value_iterator& _iter) noexcept - : iter{ _iter } - {} +simdjson_inline object_iterator::object_iterator(const value_iterator &_iter) noexcept + : iter{_iter} +{} - simdjson_inline simdjson_result object_iterator::operator*() noexcept { - 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_inline simdjson_result 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 { - // 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_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 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 + 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. - // +// +// ### 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 ondemand +} // namespace westmere } // namespace simdjson namespace simdjson { - simdjson_inline simdjson_result::simdjson_result( - westmere::ondemand::object_iterator&& value - ) noexcept - : implementation_simdjson_result_base(std::forward(value)) - { - first.iter.assert_is_valid(); - } - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base({}, error) - { - } +simdjson_inline simdjson_result::simdjson_result( + westmere::ondemand::object_iterator &&value +) noexcept + : implementation_simdjson_result_base(std::forward(value)) +{ + first.iter.assert_is_valid(); +} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base({}, error) +{ +} - simdjson_inline simdjson_result simdjson_result::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::operator==(const simdjson_result& 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::operator!=(const simdjson_result& other) const noexcept { - if (!first.iter.is_valid()) { return error(); } - return first != other.first; - } - // Checks for ']' and ',' - simdjson_inline simdjson_result& simdjson_result::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 simdjson_result simdjson_result::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::operator==(const simdjson_result &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::operator!=(const simdjson_result &other) const noexcept { + if (!first.iter.is_valid()) { return error(); } + return first != other.first; +} +// Checks for ']' and ',' +simdjson_inline simdjson_result &simdjson_result::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 @@ -94578,193 +138781,225 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace ondemand { +namespace westmere { +namespace ondemand { - simdjson_inline parser::parser(size_t max_capacity) noexcept - : _max_capacity{ max_capacity } { - } +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; } +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; - size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * new_capacity / 3 + SIMDJSON_PADDING, 64); - string_buf.reset(new (std::nothrow) uint8_t[string_capacity]); + // string_capacity copied from document::allocate + _capacity = 0; + 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]); + 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 (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()); - } +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 parser::iterate(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(padded_string_view json) & noexcept { + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } - json.remove_utf8_bom(); + json.remove_utf8_bom(); - // Allocate if needed - if (capacity() < json.length() || !string_buf) { - SIMDJSON_TRY(allocate(json.length(), max_depth())); - } + // 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(json.data()), json.length(), stage1_mode::regular)); - return document::start({ reinterpret_cast(json.data()), this }); - } + // Run stage 1. + SIMDJSON_TRY( implementation->stage1(reinterpret_cast(json.data()), json.length(), stage1_mode::regular) ); + return document::start({ reinterpret_cast(json.data()), this }); +} #ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON - simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept { + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } - json.remove_utf8_bom(); + json.remove_utf8_bom(); - // Allocate if needed - if (capacity() < json.length() || !string_buf) { - SIMDJSON_TRY(allocate(json.length(), max_depth())); - } + // 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(json.data()), json.length(), stage1_mode::regular); - if (err) { - if (err != UNCLOSED_STRING) - return err; - } - return document::start({ reinterpret_cast(json.data()), this, true }); - } + // Run stage 1. + const simdjson::error_code err = implementation->stage1(reinterpret_cast(json.data()), json.length(), stage1_mode::regular); + if (err) { + if (err != UNCLOSED_STRING) + return err; + } + return document::start({ reinterpret_cast(json.data()), this, true }); +} #endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON - simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 parser::iterate(std::string_view json, size_t allocated) & noexcept { - return iterate(padded_string_view(json, allocated)); - } +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(std::string_view json, size_t allocated) & noexcept { + return iterate(padded_string_view(json, allocated)); +} - simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(std::string& json) & noexcept { - if (json.capacity() - json.size() < SIMDJSON_PADDING) { - json.reserve(json.size() + SIMDJSON_PADDING); - } - return iterate(padded_string_view(json)); - } +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(std::string &json) & noexcept { + return iterate(pad_with_reserve(json)); +} - simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const std::string& json) & noexcept { - return iterate(padded_string_view(json)); - } +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const std::string &json) & noexcept { + return iterate(padded_string_view(json)); +} - simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const simdjson_result& result) & noexcept { - // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 parser::iterate(const simdjson_result &result) & noexcept { + // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 parser::iterate(const simdjson_result& result) & noexcept { - // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 parser::iterate(const simdjson_result &result) & noexcept { + // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 parser::iterate_raw(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_raw(padded_string_view json) & noexcept { + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } - json.remove_utf8_bom(); + json.remove_utf8_bom(); - // Allocate if needed - if (capacity() < json.length()) { - SIMDJSON_TRY(allocate(json.length(), max_depth())); - } + // Allocate if needed + if (capacity() < json.length()) { + SIMDJSON_TRY( allocate(json.length(), max_depth()) ); + } - // Run stage 1. - SIMDJSON_TRY(implementation->stage1(reinterpret_cast(json.data()), json.length(), stage1_mode::regular)); - return json_iterator(reinterpret_cast(json.data()), this); - } + // Run stage 1. + SIMDJSON_TRY( implementation->stage1(reinterpret_cast(json.data()), json.length(), stage1_mode::regular) ); + return json_iterator(reinterpret_cast(json.data()), this); +} - inline simdjson_result parser::iterate_many(const uint8_t* buf, size_t len, size_t batch_size, bool allow_comma_separated) 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 (allow_comma_separated && batch_size < len) { batch_size = len; } - return document_stream(*this, buf, len, batch_size, allow_comma_separated); - } - inline simdjson_result parser::iterate_many(const char* buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept { - return iterate_many(reinterpret_cast(buf), len, batch_size, allow_comma_separated); - } - inline simdjson_result parser::iterate_many(const std::string& s, size_t batch_size, bool allow_comma_separated) noexcept { - return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated); - } - inline simdjson_result parser::iterate_many(const padded_string& s, size_t batch_size, bool allow_comma_separated) noexcept { - return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated); - } +inline simdjson_result 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; + } + if(allow_comma_separated && batch_size < len) { batch_size = len; } + return document_stream(*this, buf, len, batch_size, allow_comma_separated); +} - 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; - } +inline simdjson_result 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(buf), len, batch_size, allow_comma_separated); +} +inline simdjson_result 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 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 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 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_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 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 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(dst), end - dst); - dst = end; - return result; - } +simdjson_inline simdjson_warn_unused simdjson_result 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(dst), end-dst); + dst = end; + return result; +} - simdjson_inline simdjson_warn_unused simdjson_result 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(dst), end - dst); - dst = end; - return result; - } +simdjson_inline simdjson_warn_unused simdjson_result 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(dst), end-dst); + dst = end; + return result; +} - } // namespace ondemand - } // namespace westmere +simdjson_inline simdjson_warn_unused ondemand::parser& parser::get_parser() { + return *parser::get_parser_instance(); +} + +simdjson_inline bool 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& parser::get_parser_instance() { + std::unique_ptr& 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& 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 parser_instance = nullptr; + return parser_instance; +} + + +} // namespace ondemand +} // namespace westmere } // namespace simdjson namespace simdjson { - simdjson_inline simdjson_result::simdjson_result(westmere::ondemand::parser&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} +simdjson_inline simdjson_result::simdjson_result(westmere::ondemand::parser &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(error) {} } // namespace simdjson @@ -94784,456 +139019,210 @@ namespace simdjson { namespace simdjson { - namespace westmere { - namespace ondemand { +namespace westmere { +namespace ondemand { - simdjson_inline raw_json_string::raw_json_string(const uint8_t* _buf) noexcept : buf{ _buf } {} +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(buf); } +simdjson_inline const char * raw_json_string::raw() const noexcept { + return reinterpret_cast(buf); +} + +simdjson_inline char raw_json_string::operator[](size_t i) const noexcept { + return reinterpret_cast(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::is_free_from_unescaped_quote(std::string_view target) noexcept { - size_t pos{ 0 }; - // if the content has no escape character, just scan through it quickly! - for (; pos < target.size() && target[pos] != '\\'; pos++) {} - // slow path may begin. - bool escaping{ false }; - for (; pos < target.size(); pos++) { - if ((target[pos] == '"') && !escaping) { - return false; - } - else if (target[pos] == '\\') { - escaping = !escaping; - } - else { - escaping = false; - } - } - 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::is_free_from_unescaped_quote(const char* target) noexcept { - size_t pos{ 0 }; - // if the content has no escape character, just scan through it quickly! - for (; target[pos] && target[pos] != '\\'; pos++) {} - // slow path may begin. - bool escaping{ false }; - for (; target[pos]; pos++) { - if ((target[pos] == '"') && !escaping) { - return false; - } - else if (target[pos] == '\\') { - escaping = !escaping; - } - else { - escaping = false; - } - } - return true; - } +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()); + } + const char * r{raw()}; + size_t pos{0}; + for(;pos < target.size();pos++) { + if(r[pos] != target[pos]) { return false; } + } + if(r[pos] != '"') { return false; } + return true; +} + +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(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(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::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()); - } - const char* r{ raw() }; - size_t pos{ 0 }; - for (; pos < target.size(); 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_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_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 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 simdjson_warn_unused simdjson_result raw_json_string::unescape(json_iterator &iter, bool allow_replacement) const noexcept { + return iter.unescape(*this, allow_replacement); +} - 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_inline simdjson_warn_unused simdjson_result raw_json_string::unescape_wobbly(json_iterator &iter) const noexcept { + return iter.unescape_wobbly(*this); +} - 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 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++; + } +} - 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 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 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 ondemand +} // namespace westmere } // namespace simdjson namespace simdjson { - simdjson_inline simdjson_result::simdjson_result(westmere::ondemand::raw_json_string&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} +simdjson_inline simdjson_result::simdjson_result(westmere::ondemand::raw_json_string &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(error) {} - simdjson_inline simdjson_result simdjson_result::raw() const noexcept { - if (error()) { return error(); } - return first.raw(); - } - simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::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 simdjson_result::unescape_wobbly(westmere::ondemand::json_iterator& iter) const noexcept { - if (error()) { return error(); } - return first.unescape_wobbly(iter); - } +simdjson_inline simdjson_result simdjson_result::raw() const noexcept { + if (error()) { return error(); } + return first.raw(); +} +simdjson_inline char simdjson_result::operator[](size_t i) const noexcept { + if (error()) { return error(); } + return first[i]; +} +simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::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 simdjson_result::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/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): #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 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 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 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 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 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 to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); - } - - inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); - } - - inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); - } - - inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); - } - - inline simdjson_result to_json_string(simdjson_result 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 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 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&& x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); - } - inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& 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 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 */ /* 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 @@ -95246,637 +139235,91 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace ondemand { +namespace westmere { +namespace ondemand { - simdjson_inline token_iterator::token_iterator( - const uint8_t* _buf, - token_position position - ) noexcept : buf{ _buf }, _position{ position } - { - } +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 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::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 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 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 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; - } +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 ondemand +} // namespace westmere } // namespace simdjson namespace simdjson { - simdjson_inline simdjson_result::simdjson_result(westmere::ondemand::token_iterator&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} +simdjson_inline simdjson_result::simdjson_result(westmere::ondemand::token_iterator &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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-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 */ - -namespace simdjson { - namespace westmere { - namespace ondemand { - - 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 value::get_array() noexcept { - return array::start(iter); - } - simdjson_inline simdjson_result value::get_object() noexcept { - return object::start(iter); - } - simdjson_inline simdjson_result value::start_or_resume_object() noexcept { - if (iter.at_start()) { - return get_object(); - } - else { - return object::resume(iter); - } - } - - simdjson_inline simdjson_result value::get_raw_json_string() noexcept { - return iter.get_raw_json_string(); - } - simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { - return iter.get_string(allow_replacement); - } - template - 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 value::get_wobbly_string() noexcept { - return iter.get_wobbly_string(); - } - simdjson_inline simdjson_result value::get_double() noexcept { - return iter.get_double(); - } - simdjson_inline simdjson_result value::get_double_in_string() noexcept { - return iter.get_double_in_string(); - } - simdjson_inline simdjson_result value::get_uint64() noexcept { - return iter.get_uint64(); - } - simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { - return iter.get_uint64_in_string(); - } - simdjson_inline simdjson_result value::get_int64() noexcept { - return iter.get_int64(); - } - simdjson_inline simdjson_result value::get_int64_in_string() noexcept { - return iter.get_int64_in_string(); - } - simdjson_inline simdjson_result value::get_bool() noexcept { - return iter.get_bool(); - } - simdjson_inline simdjson_result value::is_null() noexcept { - return iter.is_null(); - } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } - - template simdjson_inline error_code value::get(T& out) noexcept { - return get().get(out); - } - -#if SIMDJSON_EXCEPTIONS - template - simdjson_inline value::operator T() noexcept(false) { - return get(); - } - 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 value::begin() & noexcept { - return get_array().begin(); - } - simdjson_inline simdjson_result value::end() & noexcept { - return {}; - } - simdjson_inline simdjson_result value::count_elements() & noexcept { - simdjson_result 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 value::count_fields() & noexcept { - simdjson_result answer; - auto a = get_object(); - answer = a.count_fields(); - iter.move_at_start(); - return answer; - } - simdjson_inline simdjson_result value::at(size_t index) noexcept { - auto a = get_array(); - return a.at(index); - } - - simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { - return start_or_resume_object().find_field(key); - } - simdjson_inline simdjson_result value::find_field(const char* key) noexcept { - return start_or_resume_object().find_field(key); - } - - simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { - return start_or_resume_object().find_field_unordered(key); - } - simdjson_inline simdjson_result value::find_field_unordered(const char* key) noexcept { - return start_or_resume_object().find_field_unordered(key); - } - - simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { - return start_or_resume_object()[key]; - } - simdjson_inline simdjson_result value::operator[](const char* key) noexcept { - return start_or_resume_object()[key]; - } - - simdjson_inline simdjson_result value::type() noexcept { - return iter.type(); - } - - simdjson_inline simdjson_result 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 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 value::is_integer() noexcept { - return iter.is_integer(); - } - simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { - return iter.get_number_type(); - } - simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { - return iter.get_number(); - } - - simdjson_inline std::string_view value::raw_json_token() noexcept { - return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); - } - - simdjson_inline simdjson_result 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 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::at_pointer(std::string_view json_pointer) noexcept { - 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::at_path(std::string_view json_path) noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) { - case json_type::array: - return (*this).get_array().at_path(json_path); - case json_type::object: - return (*this).get_object().at_path(json_path); - default: - return INVALID_JSON_POINTER; - } - } - - } // namespace ondemand - } // namespace westmere -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result( - westmere::ondemand::value&& value - ) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) - { - } - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept : - implementation_simdjson_result_base(error) - { - } - simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); - } - simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); - } - simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() & noexcept { - if (error()) { return error(); } - return {}; - } - - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::find_field(const char* key) noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char* key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](const char* key) noexcept { - if (error()) { return error(); } - return first[key]; - } - - simdjson_inline simdjson_result simdjson_result::get_array() noexcept { - if (error()) { return error(); } - return first.get_array(); - } - simdjson_inline simdjson_result simdjson_result::get_object() noexcept { - if (error()) { return error(); } - return first.get_object(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); - } - simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); - } - simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); - } - template - simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(receiver, allow_replacement); - } - simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); - } - simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); - } - simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); - } - simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); - } - - template simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return first.get(); - } - template simdjson_inline error_code simdjson_result::get(T& out) noexcept { - if (error()) { return error(); } - return first.get(out); - } - - template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return std::move(first); - } - template<> simdjson_inline error_code simdjson_result::get(westmere::ondemand::value& out) noexcept { - if (error()) { return error(); } - out = first; - return SUCCESS; - } - - simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); - } - simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); - } - simdjson_inline simdjson_result simdjson_result::is_string() noexcept { - if (error()) { return error(); } - return first.is_string(); - } - simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); - } - simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); - } - simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); - } - simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); - } -#if SIMDJSON_EXCEPTIONS - template - simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return static_cast(first); - } - simdjson_inline simdjson_result::operator westmere::ondemand::array() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator westmere::ondemand::object() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator westmere::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } -#endif - - simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); - } - - simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { - if (error()) { return error(); } - return first.raw_json(); - } - - simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); - } - - simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { - if (error()) { return error(); } - return first.current_depth(); - } - - simdjson_inline simdjson_result simdjson_result::at_pointer( - std::string_view json_pointer) noexcept { - if (error()) { - return error(); - } - return first.at_pointer(json_pointer); - } - - simdjson_inline simdjson_result simdjson_result::at_path( - std::string_view json_path) noexcept { - if (error()) { - return error(); - } - return first.at_path(json_path); - } - -} // namespace simdjson - -#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H -/* end file simdjson/generic/ondemand/value-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 @@ -95893,1089 +139336,2283 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace westmere { - namespace ondemand { +namespace westmere { +namespace ondemand { - 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_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 value_iterator::start_object() noexcept { - SIMDJSON_TRY(start_container('{', "Not an object", "object")); - return started_object(); - } +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_object() noexcept { + SIMDJSON_TRY( start_container('{', "Not an object", "object") ); + return started_object(); +} - simdjson_warn_unused simdjson_inline simdjson_result 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 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 value_iterator::started_object() noexcept { - assert_at_container_start(); +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_object() noexcept { + assert_at_container_start(); #if SIMDJSON_DEVELOPMENT_CHECKS - _json_iter->set_start_position(_depth, start_position()); + _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(); - end_container(); - return false; - } - return true; - } + 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 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 value_iterator::started_root_object() noexcept { - auto error = check_root_object(); - if (error) { return error; } - return started_object(); - } +simdjson_warn_unused simdjson_inline simdjson_result 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 { +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"); } + 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; - } + _json_iter->ascend_to(depth()-1); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline simdjson_result value_iterator::has_next_field() noexcept { - assert_at_next(); +simdjson_warn_unused simdjson_inline simdjson_result 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"); + } +} - // 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 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; - simdjson_warn_unused simdjson_inline simdjson_result 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()) { + // + // 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; } + // 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; + 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; } + // 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; } + 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; - } + } + 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; } - } + // 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; - } + // 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 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; +SIMDJSON_PUSH_DISABLE_WARNINGS +SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING +simdjson_warn_unused simdjson_inline simdjson_result 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(); + // 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; + // 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()) { + // 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; } + // 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; } + 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; } + 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 + // 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; } + // 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; - } + // 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; } + // 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 + // 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); + // 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; - } + // 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 + // 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 value_iterator::field_key() noexcept { - assert_at_next(); +simdjson_warn_unused simdjson_inline simdjson_result 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); - } + 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_value() noexcept { - assert_at_next(); +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; - } + 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 value_iterator::start_array() noexcept { - SIMDJSON_TRY(start_container('[', "Not an array", "array")); - return started_array(); - } +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_array() noexcept { + SIMDJSON_TRY( start_container('[', "Not an array", "array") ); + return started_array(); +} - simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_root_array() noexcept { - SIMDJSON_TRY(start_container('[', "Not an array", "array")); - return started_root_array(); - } +simdjson_warn_unused simdjson_inline simdjson_result 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; - } +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 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); +simdjson_warn_unused simdjson_inline simdjson_result 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()); + _json_iter->set_start_position(_depth, start_position()); #endif - return true; - } + 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 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 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 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 value_iterator::has_next_element() noexcept { - assert_at_next(); +simdjson_warn_unused simdjson_inline simdjson_result 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"); - } - } + 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 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(!not_true); - } - simdjson_warn_unused simdjson_inline simdjson_result 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 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(!not_true); +} +simdjson_warn_unused simdjson_inline simdjson_result 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 value_iterator::get_string(bool allow_replacement) noexcept { - return get_raw_json_string().unescape(json_iter(), allow_replacement); - } - template - simdjson_warn_unused simdjson_inline error_code value_iterator::get_string(string_type& receiver, bool allow_replacement) noexcept { - std::string_view content; - auto err = get_string(allow_replacement).get(content); - if (err) { return err; } - receiver = content; - return SUCCESS; - } - simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_wobbly_string() noexcept { - return get_raw_json_string().unescape_wobbly(json_iter()); - } - simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_raw_json_string() noexcept { - auto json = peek_scalar("string"); - if (*json != '"') { return incorrect_type_error("Not a string"); } - advance_scalar("string"); - return raw_json_string(json + 1); - } - simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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 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 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 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 value_iterator::is_integer() noexcept { - return numberparsing::is_integer(peek_non_root_scalar("integer")); - } - simdjson_inline simdjson_result value_iterator::get_number_type() noexcept { - return numberparsing::get_number_type(peek_non_root_scalar("integer")); - } - simdjson_inline simdjson_result 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_warn_unused simdjson_inline simdjson_result value_iterator::get_string(bool allow_replacement) noexcept { + return get_raw_json_string().unescape(json_iter(), allow_replacement); +} +template +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; } + 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 value_iterator::get_wobbly_string() noexcept { + return get_raw_json_string().unescape_wobbly(json_iter()); +} +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_raw_json_string() noexcept { + auto json = peek_scalar("string"); + if (*json != '"') { return incorrect_type_error("Not a string"); } + advance_scalar("string"); + return raw_json_string(json+1); +} +simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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 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 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 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 value_iterator::is_integer() noexcept { + return numberparsing::is_integer(peek_non_root_scalar("integer")); +} +simdjson_inline simdjson_result value_iterator::get_number_type() noexcept { + return numberparsing::get_number_type(peek_non_root_scalar("integer")); +} +simdjson_inline simdjson_result 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 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 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 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.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 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.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 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 - 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; - auto err = get_root_string(check_trailing, allow_replacement).get(content); - if (err) { return err; } - receiver = content; - return SUCCESS; - } - simdjson_warn_unused simdjson_inline simdjson_result 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 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; } - advance_scalar("string"); - return raw_json_string(json + 1); - } - simdjson_warn_unused simdjson_inline simdjson_result 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 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 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; - } +simdjson_inline simdjson_result 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.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 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.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 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 +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; } + 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 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 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; } + advance_scalar("string"); + return raw_json_string(json+1); +} +simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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(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 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 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.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; - } + 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 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.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 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.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 value_iterator::get_root_bool(bool check_trailing) noexcept { - auto max_len = peek_root_length(); - auto json = peek_root_scalar("bool"); - uint8_t tmpbuf[5 + 1 + 1]; // +1 for null termination - tmpbuf[5 + 1] = '\0'; // make sure that buffer is always null terminated. - if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 5 + 1)) { return incorrect_type_error("Not a boolean"); } - auto result = parse_bool(tmpbuf); - if (result.error() == SUCCESS) { - if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; } - advance_root_scalar("bool"); - } - return result; - } - simdjson_inline simdjson_result 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"); - } - return result; - } +simdjson_warn_unused simdjson_inline simdjson_result 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.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 error_code value_iterator::skip_child() noexcept { - SIMDJSON_ASSUME(_json_iter->token._position > _start_position); - SIMDJSON_ASSUME(_json_iter->_depth >= _depth); +simdjson_warn_unused simdjson_inline simdjson_result 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; +} - return _json_iter->skip_child(depth()); - } +simdjson_inline simdjson_result 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_inline value_iterator value_iterator::child() const noexcept { - assert_at_child(); - return { _json_iter, depth() + 1, _json_iter->token.position() }; - } +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 ); - // 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 + return _json_iter->skip_child(depth()); +} - simdjson_inline bool value_iterator::at_end() const noexcept { - return _json_iter->at_end(); - } +simdjson_inline value_iterator value_iterator::child() const noexcept { + assert_at_child(); + return { _json_iter, depth()+1, _json_iter->token.position() }; +} - simdjson_inline bool value_iterator::at_start() const noexcept { - return _json_iter->token.position() == start_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_first_field() const noexcept { - SIMDJSON_ASSUME(_json_iter->token._position > _start_position); - return _json_iter->token.position() == start_position() + 1; - } +simdjson_inline bool value_iterator::at_end() const noexcept { + return _json_iter->at_end(); +} - simdjson_inline void value_iterator::abandon() noexcept { - _json_iter->abandon(); - } +simdjson_inline bool value_iterator::at_start() const noexcept { + return _json_iter->token.position() == start_position(); +} - 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 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 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 void value_iterator::abandon() noexcept { + _json_iter->abandon(); +} - 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(); } +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; +} - // 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 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 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; } +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(); - _json_iter->return_current_and_advance(); - _json_iter->ascend_to(depth() - 1); - } + // 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 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()) { +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; } + 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(); - } + 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; - } + 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(); } +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_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(); - } + 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; } +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_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); - } + 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 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_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. - return _depth == _json_iter->_depth && *_json_iter->peek() == '"'; - } +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; - } +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_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_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); - } +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_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::move_at_container_start() noexcept { + _json_iter->_depth = _depth; + _json_iter->token.set_position(_start_position + 1); +} - simdjson_inline simdjson_result value_iterator::reset_array() noexcept { - if (error()) { return error(); } - move_at_container_start(); - return started_array(); - } +simdjson_inline simdjson_result value_iterator::reset_array() noexcept { + if(error()) { return error(); } + move_at_container_start(); + return started_array(); +} - simdjson_inline simdjson_result value_iterator::reset_object() noexcept { - if (error()) { return error(); } - move_at_container_start(); - return started_object(); - } +simdjson_inline simdjson_result 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_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_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_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); - } +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 bool value_iterator::is_valid() const noexcept { + return _json_iter != nullptr; +} - simdjson_inline simdjson_result 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 TAPE_ERROR; - } - } +simdjson_inline simdjson_result 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::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::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::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 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); - } +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 ondemand +} // namespace westmere } // namespace simdjson namespace simdjson { - simdjson_inline simdjson_result::simdjson_result(westmere::ondemand::value_iterator&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} +simdjson_inline simdjson_result::simdjson_result(westmere::ondemand::value_iterator &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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 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 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 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 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 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 to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result 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 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 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&& x) { + if (x.error()) { throw simdjson::simdjson_error(x.error()); } + return (out << x.value()); +} +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& 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 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 emails; }; + * + * std::string email; + * path_accessor::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 +#include +#include + +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 +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 +consteval auto make_field_step(const char (&name)[N]) { + return path_step(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 +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 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 +struct path_accessor { + using value = ::simdjson::westmere::ondemand::value; + + static constexpr auto parser = json_path_parser(); + 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 + static inline simdjson_result access(DocOrValue& doc_or_val) noexcept { + // Validate path at compile time if T is a struct + if constexpr (std::is_class_v) { + 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(doc_or_val.get_value()); + } + + // Extract value at path directly into target with compile-time type validation + // Example: std::string name; path_accessor::extract_field(doc, name); + template + static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept { + static_assert(std::is_class_v, "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(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 + static consteval std::enable_if_t, 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 + static inline error_code extract_with_reflection(simdjson_result 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(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(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_value, target_ref); + } + } + } + // Skip unexpected characters and continue + else { + return extract_with_reflection(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 + static inline simdjson_result access_impl(simdjson_result 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_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_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_value); + } + } else { + return access_impl(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(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 + 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 + 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 + static consteval bool is_container_type() { + using BaseType = std::remove_cvref_t; + if constexpr (requires { typename BaseType::value_type; }) { + return true; + } + if constexpr (std::is_array_v) { + return true; + } + return false; + } + + // Extract element type from non-reflected container + template + using extract_element_type = std::conditional_t< + requires { typename std::remove_cvref_t::value_type; }, + typename std::remove_cvref_t::value_type, + std::conditional_t< + std::is_array_v>, + std::remove_extent_t>, + void + > + >; + + // Validate path matches struct definition + static consteval bool validate_path() { + if constexpr (!std::is_class_v) { + 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 +inline simdjson_result<::simdjson::westmere::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = path_accessor; + return accessor::access(doc_or_val); +} + +// Overload without type parameter (no validation) +template +inline simdjson_result<::simdjson::westmere::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = path_accessor; + return accessor::access(doc_or_val); +} + +// ============================================================================ +// JSON Pointer Compile-Time Support (RFC 6901) +// ============================================================================ + +// JSON Pointer parser: /field/0/nested (slash-separated) +template +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 +struct pointer_accessor { + using parser = json_pointer_parser; + 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) { + 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::is_array_like_reflected(current_type)) { + return false; + } + current_type = path_accessor::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 + static inline simdjson_result access_impl(simdjson_result 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(next_value); + } else { + auto obj = current.get_object().value_unsafe(); + auto next_value = obj.find_field_unordered(token); + return access_impl(next_value); + } + } + } + + // Access JSON value at pointer + template + static inline simdjson_result access(DocOrValue& doc_or_val) noexcept { + if constexpr (std::is_class_v) { + 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 current = doc_or_val.get_value(); + return access_impl<0>(current); + } + + // Extract value at pointer directly into target with type validation + template + static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept { + static_assert(std::is_class_v, "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 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 + static consteval std::enable_if_t, 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::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 +inline simdjson_result<::simdjson::westmere::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = pointer_accessor; + return accessor::access(doc_or_val); +} + +// Overload without type parameter (no validation) +template +inline simdjson_result<::simdjson::westmere::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = pointer_accessor; + 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 */ @@ -97013,21 +141650,21 @@ SIMDJSON_UNTARGET_REGION /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - /** - * Implementation for LSX. - */ - namespace lsx { +/** + * Implementation for LSX. + */ +namespace lsx { - class implementation; +class implementation; - namespace { - namespace simd { - template struct simd8; - template struct simd8x64; - } // namespace simd - } // unnamed namespace +namespace { +namespace simd { +template struct simd8; +template struct simd8x64; +} // namespace simd +} // unnamed namespace - } // namespace lsx +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_LSX_BASE_H @@ -97041,8 +141678,6 @@ namespace simdjson { /* amalgamation skipped (editor-only): #include "simdjson/lsx/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 static_assert(sizeof(__m128i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for LoongArch SX"); @@ -97061,43 +141696,43 @@ static_assert(sizeof(__m128i) <= simdjson::SIMDJSON_PADDING, "insufficient paddi /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace { +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); - } +// 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 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 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); - } +/* 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); +} - simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t* result) { - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); - } +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t *result) { + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); +} - } // unnamed namespace - } // namespace lsx +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_LSX_BITMANIPULATION_H @@ -97112,26 +141747,26 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace { +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; - } +// +// 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 +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif @@ -97150,33 +141785,39 @@ namespace simdjson { #include namespace simdjson { - namespace lsx { - namespace numberparsing { +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); - } +// 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; - } +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 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 */ @@ -97192,347 +141833,348 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace { - namespace simd { +namespace lsx { +namespace { +namespace simd { - // Forward-declared so they can be used by splat and friends. - template - struct base { - __m128i value; + // Forward-declared so they can be used by splat and friends. + template + struct base { + __m128i value; - // Zero constructor - simdjson_inline base() : value{ __m128i() } {} + // Zero constructor + simdjson_inline base() : value{__m128i()} {} - // Conversion from SIMD register - simdjson_inline base(const __m128i _value) : value(_value) {} + // 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; } + // 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(this); *this_cast = *this_cast | other; return *this_cast; } - simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } - simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } - }; + // 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(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; - // Forward-declared so they can be used by splat and friends. - template - struct simd8; + // Forward-declared so they can be used by splat and friends. + template + struct simd8; - template> - struct base8 : base> { - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m128i _value) : base>(_value) {} + template> + struct base8: base> { + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m128i _value) : base>(_value) {} - friend simdjson_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return __lsx_vseq_b(lhs, rhs); } + friend simdjson_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return __lsx_vseq_b(lhs, rhs); } - static const int SIZE = sizeof(base>::value); + static const int SIZE = sizeof(base>::value); - template - simdjson_inline simd8 prev(const simd8 prev_chunk) const { - return __lsx_vor_v(__lsx_vbsll_v(*this, N), __lsx_vbsrl_v(prev_chunk, 16 - N)); - } - }; + template + simdjson_inline simd8 prev(const simd8 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 : base8 { - static simdjson_inline simd8 splat(bool _value) { - return __lsx_vreplgr2vr_b(uint8_t(-(!!_value))); - } + // SIMD byte mask type (returned by things like eq and gt) + template<> + struct simd8: base8 { + static simdjson_inline simd8 splat(bool _value) { + return __lsx_vreplgr2vr_b(uint8_t(-(!!_value))); + } - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m128i _value) : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) : base8(splat(_value)) {} + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m128i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(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 operator~() const { return *this ^ true; } - }; + 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 operator~() const { return *this ^ true; } + }; - template - struct base8_numeric : base8 { - static simdjson_inline simd8 splat(T _value) { return __lsx_vreplgr2vr_b(_value); } - static simdjson_inline simd8 zero() { return __lsx_vldi(0); } - static simdjson_inline simd8 load(const T values[16]) { - return __lsx_vld(reinterpret_cast(values), 0); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdjson_inline simd8 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + template + struct base8_numeric: base8 { + static simdjson_inline simd8 splat(T _value) { return __lsx_vreplgr2vr_b(_value); } + static simdjson_inline simd8 zero() { return __lsx_vldi(0); } + static simdjson_inline simd8 load(const T values[16]) { + return __lsx_vld(reinterpret_cast(values), 0); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - simdjson_inline base8_numeric() : base8() {} - simdjson_inline base8_numeric(const __m128i _value) : base8(_value) {} + simdjson_inline base8_numeric() : base8() {} + simdjson_inline base8_numeric(const __m128i _value) : base8(_value) {} - // Store to array - simdjson_inline void store(T dst[16]) const { - return __lsx_vst(*this, reinterpret_cast<__m128i*>(dst), 0); - } + // 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 operator+(const simd8 other) const { return __lsx_vadd_b(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return __lsx_vsub_b(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { return __lsx_vadd_b(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return __lsx_vsub_b(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } - // Override to distinguish from bool version - simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return __lsx_vshuf_b(lookup_table, lookup_table, *this); - } + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 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 - 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(reinterpret_cast(pshufb_combine_table)), pop1 * 8); - __m128i answer = __lsx_vshuf_b(pruned, pruned, compactmask); - __lsx_vst(answer, reinterpret_cast(output), 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. + template + 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(reinterpret_cast(pshufb_combine_table)), pop1 * 8); + __m128i answer = __lsx_vshuf_b(pruned, pruned, compactmask); + __lsx_vst(answer, reinterpret_cast(output), 0); + } - template - simdjson_inline simd8 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::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - }; + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; - // Signed bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + // Signed bytes + template<> + struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - // Order-sensitive comparisons - simdjson_inline simd8 max_val(const simd8 other) const { return __lsx_vmax_b(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return __lsx_vmin_b(*this, other); } - simdjson_inline simd8 operator>(const simd8 other) const { return __lsx_vslt_b(other, *this); } - simdjson_inline simd8 operator<(const simd8 other) const { return __lsx_vslt_b(*this, other); } - }; + // Order-sensitive comparisons + simdjson_inline simd8 max_val(const simd8 other) const { return __lsx_vmax_b(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return __lsx_vmin_b(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return __lsx_vslt_b(other, *this); } + simdjson_inline simd8 operator<(const simd8 other) const { return __lsx_vslt_b(*this, other); } + }; - // Unsigned bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15 - ); - } + // Unsigned bytes + template<> + struct simd8: base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m128i _value) : base8_numeric(_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 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( + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10,v11,v12,v13,v14,v15 + ); + } - // Saturated math - simdjson_inline simd8 saturating_add(const simd8 other) const { return __lsx_vsadd_bu(*this, other); } - simdjson_inline simd8 saturating_sub(const simd8 other) const { return __lsx_vssub_bu(*this, other); } + // Saturated math + simdjson_inline simd8 saturating_add(const simd8 other) const { return __lsx_vsadd_bu(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return __lsx_vssub_bu(*this, other); } - // Order-specific operations - simdjson_inline simd8 max_val(const simd8 other) const { return __lsx_vmax_bu(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return __lsx_vmin_bu(other, *this); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } - simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } - simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } - simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } + // Order-specific operations + simdjson_inline simd8 max_val(const simd8 other) const { return __lsx_vmax_bu(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return __lsx_vmin_bu(other, *this); } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } + simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } + simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } + simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } - // Bit-specific operations - simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } - simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } - simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } - simdjson_inline simd8 any_bits_set(simd8 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 bits) const { - return 0 == __lsx_vpickve2gr_hu(__lsx_vmsknz_b(__lsx_vand_v(*this, bits)), 0); - } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } - template - simdjson_inline simd8 shr() const { return simd8(__lsx_vsrli_b(*this, N)); } - template - simdjson_inline simd8 shl() const { return simd8(__lsx_vslli_b(*this, N)); } - }; + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } + simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } + simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } + simdjson_inline simd8 any_bits_set(simd8 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 bits) const { + return 0 == __lsx_vpickve2gr_hu(__lsx_vmsknz_b(__lsx_vand_v(*this, bits)), 0); + } + simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } + template + simdjson_inline simd8 shr() const { return simd8(__lsx_vsrli_b(*this, N)); } + template + simdjson_inline simd8 shl() const { return simd8(__lsx_vslli_b(*this, N)); } + }; - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 4, "LSX kernel should use four registers per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 4, "LSX kernel should use four registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{ chunk0, chunk1, chunk2, chunk3 } {} - simdjson_inline simd8x64(const T ptr[64]) : chunks{ simd8::load(ptr), simd8::load(ptr + 16), simd8::load(ptr + 32), simd8::load(ptr + 48) } {} + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::load(ptr+16), simd8::load(ptr+32), simd8::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(output); - // There should be a critical value which processes in scaler is faster. - if (zcnt1) - this->chunks[0].compress(mask1, reinterpret_cast(voutput)); - voutput += zcnt1; - if (zcnt2) - this->chunks[1].compress(mask2, reinterpret_cast(voutput)); - voutput += zcnt2; - if (zcnt3) - this->chunks[2].compress(mask3, reinterpret_cast(voutput)); - voutput += zcnt3; - if (zcnt4) - this->chunks[3].compress(mask4, reinterpret_cast(voutput)); - voutput += zcnt4; - return reinterpret_cast(voutput) - reinterpret_cast(output); - } + 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(output); + // There should be a critical value which processes in scaler is faster. + if (zcnt1) + this->chunks[0].compress(mask1, reinterpret_cast(voutput)); + voutput += zcnt1; + if (zcnt2) + this->chunks[1].compress(mask2, reinterpret_cast(voutput)); + voutput += zcnt2; + if (zcnt3) + this->chunks[2].compress(mask3, reinterpret_cast(voutput)); + voutput += zcnt3; + if (zcnt4) + this->chunks[3].compress(mask4, reinterpret_cast(voutput)); + voutput += zcnt4; + return reinterpret_cast(voutput) - reinterpret_cast(output); + } - simdjson_inline void store(T ptr[64]) const { - this->chunks[0].store(ptr + sizeof(simd8) * 0); - this->chunks[1].store(ptr + sizeof(simd8) * 1); - this->chunks[2].store(ptr + sizeof(simd8) * 2); - this->chunks[3].store(ptr + sizeof(simd8) * 3); - } + simdjson_inline void store(T ptr[64]) const { + this->chunks[0].store(ptr+sizeof(simd8)*0); + this->chunks[1].store(ptr+sizeof(simd8)*1); + this->chunks[2].store(ptr+sizeof(simd8)*2); + this->chunks[3].store(ptr+sizeof(simd8)*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 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 reduce_or() const { - return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]); - } + simdjson_inline simd8 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] == mask, - this->chunks[1] == mask, - this->chunks[2] == mask, - this->chunks[3] == mask - ).to_bitmask(); - } + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + 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& other) const { - return simd8x64( - 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 eq(const simd8x64 &other) const { + return simd8x64( + 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] <= mask, - this->chunks[1] <= mask, - this->chunks[2] <= mask, - this->chunks[3] <= mask - ).to_bitmask(); - } - }; // struct simd8x64 + simdjson_inline uint64_t lteq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] <= mask, + this->chunks[1] <= mask, + this->chunks[2] <= mask, + this->chunks[3] <= mask + ).to_bitmask(); + } + }; // struct simd8x64 - } // namespace simd - } // unnamed namespace - } // namespace lsx +} // namespace simd +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_LSX_SIMD_H @@ -97549,46 +142191,71 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace { +namespace lsx { +namespace { - using namespace simd; +using namespace simd; - // Holds backslashes and quotes locations. - struct backslash_and_quote { - public: - static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t* src, uint8_t* dst); +// 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); } + 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 + 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 v0(src); - simd8 v1(src + sizeof(v0)); - v0.store(dst); - v1.store(dst + sizeof(v0)); +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 v0(src); + simd8 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(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); - return { - uint32_t(bs_and_quote), // bs_bits - uint32_t(bs_and_quote >> 32) // quote_bits - }; - } + // 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(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); + return { + uint32_t(bs_and_quote), // bs_bits + uint32_t(bs_and_quote >> 32) // quote_bits + }; +} - } // unnamed namespace - } // namespace lsx + +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 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + static_cast((is_backslash | is_quote | is_control).to_bitmask()) + }; +} + +} // unnamed namespace +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_LSX_STRINGPARSING_DEFS_H @@ -97598,7 +142265,7 @@ namespace simdjson { /* 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) +#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_BUILDER_DEPENDENCIES_H) #error simdjson/generic/ondemand/dependencies.h must be included before simdjson/generic/ondemand/amalgamated.h! #endif @@ -97613,46 +142280,141 @@ namespace simdjson { /* 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 { +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; +/** 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; +/** @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*; +/** @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; +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; - } // namespace ondemand - } // namespace lsx +} // 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 struct is_builtin_deserializable : std::false_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; + +template +concept is_builtin_deserializable_v = is_builtin_deserializable::value; + +template +concept custom_deserializable = tag_invocable; + +template +concept deserializable = custom_deserializable || is_builtin_deserializable_v || concepts::optional_type; + +template +concept nothrow_custom_deserializable = nothrow_tag_invocable; + +// built-in types are noexcept and if an error happens, the value simply gets ignored and the error is returned. +template +concept nothrow_deserializable = nothrow_custom_deserializable || is_builtin_deserializable_v; + +/// 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 + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(array_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for object + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(object_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for value + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document reference + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + 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 @@ -97664,485 +142426,490 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace ondemand { +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{}; +/** + * 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; +public: + simdjson_inline value_iterator() noexcept = default; - /** - * Denote that we're starting a document. - */ - simdjson_inline void start_document() noexcept; + /** + * 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; + /** + * 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 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 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 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; + /** + * 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; + /** + * 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 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 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 type() 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 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. - * - * @{ - */ + /** + * @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 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 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). - */ - simdjson_warn_unused simdjson_inline simdjson_result 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). - */ - simdjson_warn_unused simdjson_inline simdjson_result started_root_object() noexcept; + /** + * 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 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 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 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 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 has_next_field() 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 has_next_field() noexcept; - /** - * Get the current field's key. - */ - simdjson_warn_unused simdjson_inline simdjson_result field_key() noexcept; + /** + * Get the current field's key. + */ + simdjson_warn_unused simdjson_inline simdjson_result field_key() noexcept; - /** - * Pass the : in the field and move to its value. - */ - simdjson_warn_unused simdjson_inline error_code field_value() 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. + * + * 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 find_field_raw(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 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 find_field_unordered_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 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. - * @{ - */ + /** + * @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 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 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). - */ - simdjson_warn_unused simdjson_inline simdjson_result 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). - */ - simdjson_warn_unused simdjson_inline simdjson_result started_root_array() noexcept; + /** + * 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 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 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 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 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 has_next_element() 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 has_next_element() noexcept; - /** - * Get a child value iterator. - */ - simdjson_warn_unused simdjson_inline value_iterator child() const noexcept; + /** + * Get a child value iterator. + */ + simdjson_warn_unused simdjson_inline value_iterator child() const noexcept; - /** @} */ + /** @} */ - /** - * @defgroup scalar Scalar values - * @addtogroup scalar - * @{ - */ + /** + * @defgroup scalar Scalar values + * @addtogroup scalar + * @{ + */ - simdjson_warn_unused simdjson_inline simdjson_result get_string(bool allow_replacement) noexcept; - template - simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_int64() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_double() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_bool() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_null() noexcept; - simdjson_warn_unused simdjson_inline bool is_negative() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_integer() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_number() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_string(bool allow_replacement) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_int64() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_double() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_bool() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result is_null() noexcept; + simdjson_warn_unused simdjson_inline bool is_negative() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result is_integer() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_number() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_string(bool check_trailing, bool allow_replacement) noexcept; - template - 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 get_root_wobbly_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_raw_json_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64_in_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_int64(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_int64_in_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_double(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_double_in_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_bool(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline bool is_root_negative() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_root_integer(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_number_type(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_number(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_root_null(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_string(bool check_trailing, bool allow_replacement) noexcept; + template + 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 get_root_wobbly_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_raw_json_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64_in_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_int64(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_int64_in_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_double(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_double_in_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_bool(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline bool is_root_negative() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result is_root_integer(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_number_type(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_number(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result is_root_null(bool check_trailing) noexcept; - 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_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; + 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 reset_array() noexcept; - /** - * Restarts an object iteration. - * @returns Whether the object has any fields (returns false for empty). - */ - simdjson_inline simdjson_result 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; + /** @} */ +protected: + /** + * Restarts an array iteration. + * @returns Whether the array has any elements (returns false for empty). + */ + simdjson_inline simdjson_result reset_array() noexcept; + /** + * Restarts an object iteration. + * @returns Whether the object has any fields (returns false for empty). + */ + simdjson_inline simdjson_result 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; + /** + * 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 parse_null(const uint8_t* json) const noexcept; - simdjson_inline simdjson_result 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; + simdjson_inline simdjson_result parse_null(const uint8_t *json) const noexcept; + simdjson_inline simdjson_result 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. - */ + /** + * 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 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_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_inline error_code start_container(uint8_t start_char, const char* incorrect_type_message, const char* type) noexcept; - simdjson_inline error_code end_container() 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 advance_to_value() 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 advance_to_value() noexcept; - simdjson_inline error_code incorrect_type_error(const char* message) const noexcept; - simdjson_inline error_code error_unless_more_tokens(uint32_t tokens = 1) const 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; + 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; + /** + * 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; + 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; + /** 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; - /** @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_inline error_code report_error(error_code error, const char* message) noexcept; + /** @copydoc error_code json_iterator::position() const noexcept; */ + simdjson_inline token_position position() const 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 array; - friend class value; - friend class field; - }; // value_iterator + 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 ondemand +} // namespace lsx } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public lsx::implementation_simdjson_result_base { - 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; - }; +template<> +struct simdjson_result : public lsx::implementation_simdjson_result_base { +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 @@ -98157,776 +142924,876 @@ namespace simdjson { /* 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 */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ +#include + 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; +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, uint64_t, int64_t, double, bool - * - * You may use get_double(), get_bool(), get_uint64(), get_int64(), - * 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 simdjson_inline simdjson_result get() noexcept { - // 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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " 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."); - } + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool + * + * You may use get_double(), get_bool(), get_uint64(), get_int64(), + * 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 + simdjson_inline simdjson_result get() +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } - /** - * Get this value as the given type. - * - * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool - * - * @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 simdjson_inline error_code get(T& out) noexcept; - /** - * 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 get_array() noexcept; + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + #if SIMDJSON_SUPPORTS_CONCEPTS + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else if constexpr (concepts::optional_type) { + using value_type = typename std::remove_cvref_t::value_type; - /** - * 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 get_object() noexcept; + // 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; + } - /** - * 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 get_uint64() noexcept; + if (!out) { + out.emplace(); + } + return get(out.value()); + } else { + static_assert(!sizeof(T), "The get 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 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, double, or bool."); + static_cast(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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " 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(out); // to get rid of unused errors + return UNINITIALIZED; +#endif + } - /** - * 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 get_uint64_in_string() noexcept; + /** + * 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 get_array() 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 get_int64() 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 get_object() 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 get_int64_in_string() 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 get_uint64() 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 get_double() 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 get_uint64_in_string() 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 get_double_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 get_int64() noexcept; - /** - * Cast this JSON value to a string. - * - * The string is guaranteed to be valid UTF-8. - * - * Equivalent to get(). - * - * Important: a value should be consumed once. Calling get_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 get_string(bool allow_replacement = false) 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 get_int64_in_string() noexcept; - /** - * 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 - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) 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 get_double() 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 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 get_raw_json_string() 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 get_double_in_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 get_bool() noexcept; + /** + * Cast this JSON value to a string. + * + * The string is guaranteed to be valid UTF-8. + * + * Equivalent to get(). + * + * 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 get_string(bool allow_replacement = false) 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 is_null() noexcept; + /** + * 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 + 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 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 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 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 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 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 - 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 signed 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(). - * - * @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); + /** + * Cast this JSON value to an instance of type T. The programmer is responsible for + * providing an implementation of get 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 + 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 signed 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(). + * + * @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 begin() & noexcept; - /** - * Sentinel representing the end of the array. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result 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 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 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 at(size_t index) noexcept; - /** - * Look up a field by name on an object (order-sensitive). - * - * 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 }`: - * - * ```c++ - * 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. + /** + * Begin array iteration. + * + * Part of the std::iterable interface. + * + * @returns INCORRECT_TYPE If the JSON value is not an array. + */ + simdjson_inline simdjson_result begin() & noexcept; + /** + * Sentinel representing the end of the array. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result 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 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 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 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 find_field(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ - simdjson_inline simdjson_result find_field(const char* key) noexcept; + * **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 find_field(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ + simdjson_inline simdjson_result 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. - * - * It is the default, however, 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 find_field_unordered(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result find_field_unordered(const char* key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](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 find_field_unordered(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result find_field_unordered(const char *key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result 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 type() noexcept; + /** + * 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 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 is_scalar() noexcept; - /** - * Checks whether the value is a string. - * - * @returns true if the type is string - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". - */ - simdjson_inline simdjson_result is_string() noexcept; + /** + * Checks whether the value is a 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 is_scalar() noexcept; + /** + * Checks whether the value is a string. + * + * @returns true if the type is string + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + */ + simdjson_inline simdjson_result is_string() noexcept; - /** - * Checks whether the value is a negative number. - * - * @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 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 get_number_type() 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 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 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 get_number() 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 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 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 raw_json() 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 raw_json() noexcept; - /** - * Returns the current location in the document if in bounds. - */ - simdjson_inline simdjson_result current_location() noexcept; + /** + * Returns the current location in the document if in bounds. + */ + simdjson_inline simdjson_result 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; + /** + * 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 at_pointer(std::string_view json_pointer) 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 at_pointer(std::string_view json_pointer) noexcept; - /** - * Get the value associated with the given JSONPath expression. We only support - * JSONPath queries that trivially convertible to JSON Pointer queries: key - * names and array indices. - * - * @return The value associated with the given JSONPath expression, or: - * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - */ - simdjson_inline simdjson_result at_path(std::string_view at_path) noexcept; + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + simdjson_inline simdjson_result at_path(std::string_view at_path) noexcept; + /** + * Get all values matching the given JSONPath expression with wildcard support. + * Supports wildcard character (*) for arrays or ".*" for objects. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern + */ + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; - protected: - /** - * Create a value. - */ - simdjson_inline value(const value_iterator& iter) 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; + /** + * 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; + /** + * 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; + /** + * 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 start_or_resume_object() noexcept; + /** + * Get the object, starting or resuming it as necessary + */ + simdjson_inline simdjson_result 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; + // simdjson_inline void log_value(const char *type) const noexcept; + // simdjson_inline void log_error(const char *message) const noexcept; - value_iterator iter{}; + value_iterator iter{}; - friend class document; - friend class array_iterator; - friend class field; - friend class object; - friend struct simdjson_result; - friend struct simdjson_result; - friend class field; - }; + friend class document; + friend class array_iterator; + friend class field; + friend class object; + friend struct simdjson_result; + friend struct simdjson_result; + friend class field; +}; - } // namespace ondemand - } // namespace lsx +} // namespace ondemand +} // namespace lsx } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public lsx::implementation_simdjson_result_base { - 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; +template<> +struct simdjson_result : public lsx::implementation_simdjson_result_base { +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 get_array() noexcept; - simdjson_inline simdjson_result get_object() noexcept; + simdjson_inline simdjson_result get_array() noexcept; + simdjson_inline simdjson_result get_object() noexcept; - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result is_null() noexcept; + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result is_null() noexcept; - template simdjson_inline simdjson_result get() noexcept; + template simdjson_inline simdjson_result get() noexcept; - template simdjson_inline error_code get(T& out) noexcept; + template simdjson_inline error_code get(T &out) noexcept; #if SIMDJSON_EXCEPTIONS - template - 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); + template + 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 count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; + simdjson_inline simdjson_result count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result end() & noexcept; - /** - * Look up a field by name on an object (order-sensitive). - * - * 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 }`: - * - * ```c++ - * 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 find_field(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ - simdjson_inline simdjson_result find_field(const char* key) 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 find_field(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ + simdjson_inline simdjson_result 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. - * - * It is the default, however, 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 find_field_unordered(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result find_field_unordered(const char* key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](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 find_field_unordered(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result find_field_unordered(const char *key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result operator[](int) noexcept = delete; - /** - * Get the type of this JSON value. - * - * 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 type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_string() noexcept; - simdjson_inline simdjson_result is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; + /** + * 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 type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + simdjson_inline simdjson_result is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; - /** @copydoc simdjson_inline std::string_view value::raw_json_token() const noexcept */ - simdjson_inline simdjson_result raw_json_token() noexcept; - simdjson_inline simdjson_result raw_json() noexcept; + /** @copydoc simdjson_inline std::string_view value::raw_json_token() const noexcept */ + simdjson_inline simdjson_result raw_json_token() noexcept; + simdjson_inline simdjson_result raw_json() noexcept; - /** @copydoc simdjson_inline simdjson_result current_location() noexcept */ - simdjson_inline simdjson_result current_location() noexcept; - /** @copydoc simdjson_inline int32_t current_depth() const noexcept */ - simdjson_inline simdjson_result current_depth() const noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - }; + /** @copydoc simdjson_inline simdjson_result current_location() noexcept */ + simdjson_inline simdjson_result current_location() noexcept; + /** @copydoc simdjson_inline int32_t current_depth() const noexcept */ + simdjson_inline simdjson_result current_depth() const noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; +}; } // namespace simdjson @@ -98942,53 +143809,53 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace ondemand { +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 { +// 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 - }; +enum class log_level : int32_t { + info = 0, + error = 1 +}; #if SIMDJSON_VERBOSE_LOGGING - static constexpr const bool LOG_ENABLED = true; + static constexpr const bool LOG_ENABLED = true; #else - static constexpr const bool LOG_ENABLED = false; + 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 - 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 - 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; +// 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 +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 +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_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; +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 logger +} // namespace ondemand +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_LOGGER_H @@ -99005,150 +143872,150 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace ondemand { +namespace lsx { +namespace ondemand { - /** - * Iterates through JSON tokens (`{` `}` `[` `]` `,` `:` `""` `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; +/** + * Iterates through JSON tokens (`{` `}` `[` `]` `,` `:` `""` `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; + /** + * 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; + /** + * 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. + // 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; + 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; +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; + /** + * 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{}; + const uint8_t *buf{}; + token_position _position{}; - friend class json_iterator; - friend class value_iterator; - friend class object; - template - friend simdjson_inline void logger::log_line(const json_iterator& iter, const char* title_prefix, const char* title, std::string_view detail, int delta, int depth_delta, logger::log_level level, Args&&... args) noexcept; - template - 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; - }; + friend class json_iterator; + friend class value_iterator; + friend class object; + template + friend simdjson_inline void logger::log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta, logger::log_level level, Args&&... args) noexcept; + template + 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 ondemand +} // namespace lsx } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public lsx::implementation_simdjson_result_base { - 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 - }; +template<> +struct simdjson_result : public lsx::implementation_simdjson_result_base { +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 @@ -99166,330 +144033,330 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace ondemand { +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 }; +/** + * 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}; - 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; +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 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; + /** + * 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; - /** - * 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; + /** + * 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 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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 depth. + */ + simdjson_inline depth_t depth() const noexcept; - /** - * Get current (writeable) location in the string buffer. - */ - simdjson_inline uint8_t*& string_buf_loc() 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_inline error_code report_error(error_code error, const char* message) 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_inline error_code optional_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; + /** + * 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 unescape(raw_json_string in, bool allow_replacement) noexcept; - simdjson_inline simdjson_result unescape_wobbly(raw_json_string in) 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 unescape(raw_json_string in, bool allow_replacement) noexcept; + simdjson_inline simdjson_result unescape_wobbly(raw_json_string in) noexcept; - simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept; + simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept; - simdjson_inline error_code consume_character(char c) 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; + 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; + /* 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 current_location() const noexcept; + /** + * Returns the current location in the document if in bounds. + */ + inline simdjson_result 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; + /** + * 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; + 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; + /// 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 - 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 - 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 + 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 + 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 + 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 ondemand +} // namespace lsx } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public lsx::implementation_simdjson_result_base { - public: - simdjson_inline simdjson_result(lsx::ondemand::json_iterator&& value) noexcept; ///< @private - simdjson_inline simdjson_result(error_code error) noexcept; ///< @private +template<> +struct simdjson_result : public lsx::implementation_simdjson_result_base { +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; - }; + simdjson_inline simdjson_result() noexcept = default; +}; } // namespace simdjson @@ -99507,152 +144374,153 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace ondemand { +namespace lsx { +namespace ondemand { - /** - * The type of a JSON value. - */ - enum class json_type { - // 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) - }; +/** + * 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 { +/** + * 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 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::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; + /** + * 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; + /** + * 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 - 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 - 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. - */ +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 + 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 + 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 }; - }; + /** + * 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; +/** + * 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& type) noexcept(false); +/** + * 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 &type) noexcept(false); #endif - } // namespace ondemand - } // namespace lsx +} // namespace ondemand +} // namespace lsx } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public lsx::implementation_simdjson_result_base { - 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 - }; +template<> +struct simdjson_result : public lsx::implementation_simdjson_result_base { +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 @@ -99669,200 +144537,206 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace ondemand { +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; +/** + * 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; + /** + * 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; - /** - * 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; + /** + * 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). - * 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 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 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). + * 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 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). + * 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 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; + /** + * 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; - /** - * 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; + /** + * 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; - private: + /** + * 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; } + /** + * 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; } + /** + * 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 unescape(json_iterator& iter, bool allow_replacement) const noexcept; + /** + * 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 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 unescape_wobbly(json_iterator& iter) const noexcept; - const uint8_t* buf{}; - friend class object; - friend class field; - friend class parser; - friend struct simdjson_result; - }; + /** + * 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 unescape_wobbly(json_iterator &iter) const noexcept; + const uint8_t * buf{}; + friend class object; + friend class field; + friend class parser; + friend struct simdjson_result; +}; - simdjson_unused simdjson_inline std::ostream& operator<<(std::ostream&, const raw_json_string&) noexcept; +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; +/** + * 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 ondemand +} // namespace lsx } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public lsx::implementation_simdjson_result_base { - 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 raw() const noexcept; - simdjson_inline simdjson_warn_unused simdjson_result unescape(lsx::ondemand::json_iterator& iter, bool allow_replacement) const noexcept; - simdjson_inline simdjson_warn_unused simdjson_result unescape_wobbly(lsx::ondemand::json_iterator& iter) const noexcept; - }; +template<> +struct simdjson_result : public lsx::implementation_simdjson_result_base { +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 raw() const noexcept; + simdjson_inline char operator[](size_t) const noexcept; + simdjson_inline simdjson_warn_unused simdjson_result unescape(lsx::ondemand::json_iterator &iter, bool allow_replacement) const noexcept; + simdjson_inline simdjson_warn_unused simdjson_result unescape_wobbly(lsx::ondemand::json_iterator &iter) const noexcept; +}; } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H @@ -99878,365 +144752,421 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ #include +#include namespace simdjson { - namespace lsx { - namespace ondemand { +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; +/** + * 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; +/** + * 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; + 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; + /** 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. - * - * @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 iterate(padded_string_view json) & noexcept; + /** + * 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 iterate(padded_string_view json) & noexcept; #ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON - simdjson_warn_unused simdjson_result iterate_allow_incomplete_json(padded_string_view json) & noexcept; + simdjson_warn_unused simdjson_result iterate_allow_incomplete_json(padded_string_view json) & noexcept; #endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const char* json, size_t len, size_t capacity) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const uint8_t* json, size_t len, size_t capacity) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(std::string_view json, size_t capacity) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const std::string& json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(std::string& json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const simdjson_result& json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const simdjson_result& json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(padded_string&& json) & noexcept = delete; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const char *json, size_t len, size_t capacity) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const uint8_t *json, size_t len, size_t capacity) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(std::string_view json, size_t capacity) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const std::string &json) & noexcept; + /** @overload simdjson_result 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 iterate(std::string &json) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const simdjson_result &json) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const simdjson_result &json) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result 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 iterate_raw(padded_string_view json) & noexcept; + /** + * @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 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. - * - * ### 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. - * @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 10MB, which has been a reasonable sweet spot in our tests. - * @param allow_comma_separated (defaults on false) This allows a mode where the documents are - * separated by commas instead of whitespace. It comes with a performance - * penalty because the entire document is indexed at once (and the document must be - * less than 4 GB), and there is no multithreading. In this mode, the batch_size parameter - * is effectively ignored, as it is set to at least the document size. - * @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 iterate_many(const uint8_t* buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ - inline simdjson_result iterate_many(const char* buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ - inline simdjson_result iterate_many(const std::string& s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - inline simdjson_result iterate_many(const std::string&& s, size_t batch_size, bool allow_comma_separated = false) = delete;// unsafe - /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ - inline simdjson_result iterate_many(const padded_string& s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - inline simdjson_result iterate_many(const padded_string&& s, size_t batch_size, bool allow_comma_separated = false) = delete;// unsafe + /** + * 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 10MB, which has been a reasonable sweet spot in our tests. + * @param allow_comma_separated (defaults on false) This allows a mode where the documents are + * separated by commas instead of whitespace. It comes with a performance + * penalty because the entire document is indexed at once (and the document must be + * less than 4 GB), and there is no multithreading. In this mode, the batch_size parameter + * is effectively ignored, as it is set to at least the document size. + * @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 iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(padded_string_view json, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_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 iterate_many(std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @private We do not want to allow implicit conversion from C string to std::string. */ + simdjson_result iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete; - /** @private We do not want to allow implicit conversion from C string to std::string. */ - simdjson_result iterate_many(const char* buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete; + /** 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; - /** 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; - /** - * 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 unescape(raw_json_string in, uint8_t *&dst, bool allow_replacement = false) const 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 }; -#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 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 unescape_wobbly(raw_json_string in, uint8_t*& dst) 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 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; + /** + * 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 - private: - /** @private [for benchmarking access] The implementation to use */ - std::unique_ptr implementation{}; - size_t _capacity{ 0 }; - size_t _max_capacity; - size_t _max_depth{ DEFAULT_MAX_DEPTH }; - std::unique_ptr string_buf{}; + /** + * 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: + friend bool release_parser(); + friend ondemand::parser& get_parser(); + /** Get the thread-local parser instance, allocates it if needed */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_parser_instance(); + /** Get the thread-local parser instance, it might be null */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_threadlocal_parser_if_exists(); + /** @private [for benchmarking access] The implementation to use */ + std::unique_ptr implementation{}; + size_t _capacity{0}; + size_t _max_capacity; + size_t _max_depth{DEFAULT_MAX_DEPTH}; + std::unique_ptr string_buf{}; + #if SIMDJSON_DEVELOPMENT_CHECKS - std::unique_ptr start_positions{}; + std::unique_ptr start_positions{}; #endif - friend class json_iterator; - friend class document_stream; - }; + friend class json_iterator; + friend class document_stream; +}; - } // namespace ondemand - } // namespace lsx +} // namespace ondemand +} // namespace lsx } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public lsx::implementation_simdjson_result_base { - 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; - }; +template<> +struct simdjson_result : public lsx::implementation_simdjson_result_base { +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 @@ -100253,211 +145183,275 @@ namespace simdjson { /* 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 */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace ondemand { +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; +/** + * 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. - */ - simdjson_inline simdjson_result begin() noexcept; - /** - * Sentinel representing the end of the array. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result 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. - * - * To check that an array is empty, it is more performant to use - * the is_empty() method. - */ - simdjson_inline simdjson_result 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 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 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 at_pointer(std::string_view json_pointer) noexcept; + /** + * Begin array iteration. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result begin() noexcept; + /** + * Sentinel representing the end of the array. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result 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 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 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 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 at_pointer(std::string_view json_pointer) noexcept; - /** - * Get the value associated with the given JSONPath expression. We only support - * JSONPath queries that trivially convertible to JSON Pointer queries: key - * names and array indices. - * - * https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00 - * - * @return The value associated with the given JSONPath expression, or: - * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - */ - inline simdjson_result at_path(std::string_view json_path) noexcept; + /** + * 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 at_path(std::string_view json_path) noexcept; - /** - * Consumes the array and returns a string_view instance corresponding to the - * array as represented in JSON. It points inside the original document. - */ - simdjson_inline simdjson_result raw_json() noexcept; + /** + * Get all values matching the given JSONPath expression with wildcard support. + * Supports wildcard patterns like "[*]" to match all array elements. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern + */ + inline simdjson_result> at_path_with_wildcard(std::string_view json_path) 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 at(size_t index) noexcept; - protected: - /** - * Go to the end of the array, no matter where you are right now. - */ - simdjson_inline error_code consume() 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 raw_json() 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 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 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 started(value_iterator& iter) 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 at(size_t index) 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; +#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 + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + 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 + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(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; - /** - * Iterator marking current position. - * - * iter.is_alive() == false indicates iteration is complete. - */ - value_iterator iter{}; + /** + * 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 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 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 started(value_iterator &iter) noexcept; - friend class value; - friend class document; - friend struct simdjson_result; - friend struct simdjson_result; - friend class array_iterator; - }; + /** + * 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; - } // namespace ondemand - } // namespace lsx + /** + * Iterator marking current position. + * + * iter.is_alive() == false indicates iteration is complete. + */ + value_iterator iter{}; + + friend class value; + friend class document; + friend struct simdjson_result; + friend struct simdjson_result; + friend class array_iterator; +}; + +} // namespace ondemand +} // namespace lsx } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public lsx::implementation_simdjson_result_base { - 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; +template<> +struct simdjson_result : public lsx::implementation_simdjson_result_base { +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 begin() noexcept; - simdjson_inline simdjson_result end() noexcept; - inline simdjson_result count_elements() & noexcept; - inline simdjson_result is_empty() & noexcept; - inline simdjson_result reset() & noexcept; - simdjson_inline simdjson_result at(size_t index) noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - simdjson_inline simdjson_result raw_json() noexcept; + simdjson_inline simdjson_result begin() noexcept; + simdjson_inline simdjson_result end() noexcept; + inline simdjson_result count_elements() & noexcept; + inline simdjson_result is_empty() & noexcept; + inline simdjson_result reset() & noexcept; + simdjson_inline simdjson_result at(size_t index) noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h - }; + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(out) ); + } + return SUCCESS; + } +#endif // SIMDJSON_SUPPORTS_CONCEPTS +}; } // namespace simdjson @@ -100469,6 +145463,7 @@ namespace simdjson { /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ /* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H */ +/* amalgamation skipped (editor-only): #include */ /* 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" */ @@ -100476,87 +145471,109 @@ namespace simdjson { namespace simdjson { - namespace lsx { - namespace ondemand { +namespace lsx { +namespace ondemand { - /** - * A forward-only JSON array. - * - * This is an input_iterator, meaning: - * - It is forward-only - * - * must be called exactly once per element. - * - ++ must be called exactly once in between each * (*, ++, *, ++, * ...) - */ - class array_iterator { - public: - /** Create a new, invalid array iterator. */ - simdjson_inline array_iterator() noexcept = default; +/** + * 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; + using difference_type = std::ptrdiff_t; + using pointer = void; + using reference = value_type; - // - // Iterator interface - // + /** Create a new, invalid array iterator. */ + simdjson_inline array_iterator() noexcept = default; - /** - * Get the current element. - * - * Part of the std::iterator interface. - */ - simdjson_inline simdjson_result 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; + // + // Iterator interface + // - private: - value_iterator iter{}; + /** + * Get the current element. + * + * Part of the std::iterator interface. + */ + simdjson_inline simdjson_result + 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; - simdjson_inline array_iterator(const value_iterator& iter) noexcept; + /** + * Check if the array is at the end. + */ + simdjson_warn_unused simdjson_inline bool at_end() const noexcept; - friend class array; - friend class value; - friend struct simdjson_result; - }; +private: +#if SIMDJSON_DEVELOPMENT_CHECKS + bool has_been_referenced{false}; +#endif + value_iterator iter{}; - } // namespace ondemand - } // namespace lsx + simdjson_inline array_iterator(const value_iterator &iter) noexcept; + + friend class array; + friend class value; + friend struct simdjson_result; +}; + +} // namespace ondemand +} // namespace lsx } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public lsx::implementation_simdjson_result_base { - public: - 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; +template<> +struct simdjson_result : public lsx::implementation_simdjson_result_base { + using iterator_category = std::input_iterator_tag; + using value_type = simdjson_result; + using difference_type = std::ptrdiff_t; + using pointer = void; + using reference = value_type; - // - // Iterator interface - // + 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; - simdjson_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. - simdjson_inline bool operator==(const simdjson_result&) const noexcept; - simdjson_inline bool operator!=(const simdjson_result&) const noexcept; - simdjson_inline simdjson_result& operator++() noexcept; - }; + // + // Iterator interface + // + + simdjson_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline bool operator==(const simdjson_result &) const noexcept; + simdjson_inline bool operator!=(const simdjson_result &) const noexcept; + simdjson_inline simdjson_result &operator++() noexcept; + + simdjson_warn_unused simdjson_inline bool at_end() const noexcept; +}; } // namespace simdjson @@ -100570,839 +145587,1050 @@ namespace simdjson { /* 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 */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + namespace simdjson { - namespace lsx { - namespace ondemand { +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; +/** + * 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 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 get_object() & noexcept; - /** - * Cast this JSON value to an unsigned integer. - * - * @returns A signed 64-bit integer. - * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. - */ - simdjson_inline simdjson_result get_uint64() noexcept; - /** - * Cast this JSON value (inside string) to an unsigned integer. - * - * @returns A signed 64-bit integer. - * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. - */ - simdjson_inline simdjson_result 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 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 get_int64_in_string() 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 get_double() noexcept; + /** + * 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 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 get_object() & noexcept; + /** + * Cast this JSON value to an unsigned integer. + * + * @returns A signed 64-bit integer. + * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. + */ + simdjson_inline simdjson_result get_uint64() noexcept; + /** + * Cast this JSON value (inside string) to an unsigned integer. + * + * @returns A signed 64-bit integer. + * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. + */ + simdjson_inline simdjson_result 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 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 get_int64_in_string() 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 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 get_double_in_string() noexcept; - /** - * 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 get_string(bool allow_replacement = false) noexcept; - /** - * 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 - 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 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 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 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 get_value() 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 get_double_in_string() noexcept; + /** + * 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 get_string(bool allow_replacement = false) noexcept; + /** + * 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 + 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 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 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 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 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 is_null() 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 is_null() noexcept; - /** - * Get this value as the given type. - * - * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool - * - * You may use get_double(), get_bool(), get_uint64(), get_int64(), - * 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 simdjson_inline simdjson_result get() & noexcept { - // 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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " 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."); - } - /** @overload template simdjson_result get() & noexcept */ - template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept { - // 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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " 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."); - } + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool + * + * You may use get_double(), get_bool(), get_uint64(), get_int64(), + * 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 + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + /** + * @overload template simdjson_result 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 + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::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(*this).get(); + } - /** - * Get this value as the given type. - * - * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 simdjson_inline error_code get(T& out) & noexcept; - /** @overload template error_code get(T &out) & noexcept */ - template simdjson_deprecated simdjson_inline error_code get(T& out) && noexcept; + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_CONCEPTS + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { + static_assert(!sizeof(T), "The get 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 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, double, or bool."); + static_cast(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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " 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(out); // to get rid of unused errors + return UNINITIALIZED; +#endif // SIMDJSON_SUPPORTS_CONCEPTS + } + + /** @overload template error_code get(T &out) & noexcept */ + template simdjson_deprecated simdjson_inline error_code get(T &out) && noexcept; #if SIMDJSON_EXCEPTIONS - /** - * Cast this JSON value to an instance of type T. The programmer is responsible for - * providing an implementation of get 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 - explicit simdjson_inline operator T() & noexcept(false); - template - explicit simdjson_deprecated simdjson_inline operator T() && noexcept(false); + /** + * Cast this JSON value to an instance of type T. The programmer is responsible for + * providing an implementation of get 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 + explicit simdjson_inline operator T() & noexcept(false); + template + 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 signed 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. - * - * @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); - /** - * 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); + /** + * 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 signed 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. + * + * @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) 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. + */ + 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. + */ + 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. - */ - simdjson_inline simdjson_result 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 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 at(size_t index) & noexcept; - /** - * Begin array iteration. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result begin() & noexcept; - /** - * Sentinel representing the end of the array. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result 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. + */ + simdjson_inline simdjson_result 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 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 at(size_t index) & noexcept; + /** + * Begin array iteration. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result begin() & noexcept; + /** + * Sentinel representing the end of the array. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result end() & noexcept; - /** - * Look up a field by name on an object (order-sensitive). - * - * 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 }`: - * - * ```c++ - * 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 find_field(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result find_field(const char* key) & 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 find_field(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result 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. - * - * It is the default, however, 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 find_field_unordered(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result find_field_unordered(const char* key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](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 find_field_unordered(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result 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). - * - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". - */ - simdjson_inline simdjson_result type() noexcept; + /** + * 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 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 is_scalar() 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 is_scalar() noexcept; - /** - * Checks whether the document is a string. - * - * @returns true if the type is string - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". - */ - simdjson_inline simdjson_result is_string() noexcept; + /** + * Checks whether the document is a string. + * + * @returns true if the type is string + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + */ + simdjson_inline simdjson_result is_string() noexcept; - /** - * Checks whether the document is a negative number. - * - * @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 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 get_number_type() 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 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 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 get_number() 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 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 raw_json_token() 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 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; + /** + * 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 current_location() const noexcept; + /** + * Returns the current location in the document if in bounds. + */ + inline simdjson_result 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 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; + /** + * 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 at_pointer(std::string_view json_pointer) 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 at_pointer(std::string_view json_pointer) noexcept; - /** - * Get the value associated with the given JSONPath expression. We only support - * JSONPath queries that trivially convertible to JSON Pointer queries: key - * names and array indices. - * - * https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00 - * - * Key values are matched exactly, without unescaping or Unicode normalization. - * We do a byte-by-byte comparison. E.g. - * - * const padded_string json = "{\"\\u00E9\":123}"_padded; - * auto doc = parser.iterate(json); - * doc.at_path(".\\u00E9") == 123 - * doc.at_path((const char*)u8".\u00E9") returns an error (NO_SUCH_FIELD) - * - * @return The value associated with the given JSONPath expression, or: - * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - */ - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + /** + * 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 at_path(std::string_view json_path) noexcept; - /** - * Consumes the document and returns a string_view instance corresponding to the - * document as represented in JSON. It points inside the original byte array containing - * the JSON document. - */ - simdjson_inline simdjson_result raw_json() noexcept; - protected: - /** - * Consumes the document. - */ - simdjson_inline error_code consume() noexcept; + /** + * Get all values matching the given JSONPath expression with wildcard support. + * + * Supports wildcard patterns like "$.array[*]" or "$.object.*" to match multiple elements. + * + * This method materializes all matching values into a vector. + * The document will be consumed after this call. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern, or: + * - INVALID_JSON_POINTER if the JSONPath cannot be parsed + * - NO_SUCH_FIELD if a field does not exist + * - INDEX_OUT_OF_BOUNDS if an array index is out of bounds + * - INCORRECT_TYPE if path traversal encounters wrong type + */ + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; - simdjson_inline document(ondemand::json_iterator&& iter) noexcept; - simdjson_inline const uint8_t* text(uint32_t idx) const 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 raw_json() noexcept; - simdjson_inline value_iterator resume_value_iterator() noexcept; - simdjson_inline value_iterator get_root_value_iterator() noexcept; - simdjson_inline simdjson_result start_or_resume_object() noexcept; - static simdjson_inline document start(ondemand::json_iterator&& iter) 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 + requires(std::is_class_v && (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; - // - // Fields - // - json_iterator iter{}; ///< Current position in the document - static constexpr depth_t DOCUMENT_DEPTH = 0; ///< document depth is always 0 + simdjson_inline document(ondemand::json_iterator &&iter) noexcept; + simdjson_inline const uint8_t *text(uint32_t idx) const noexcept; - 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; - }; + simdjson_inline value_iterator resume_value_iterator() noexcept; + simdjson_inline value_iterator get_root_value_iterator() noexcept; + simdjson_inline simdjson_result 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. - */ - 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 get_array() & noexcept; - simdjson_inline simdjson_result get_object() & noexcept; - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result get_value() noexcept; +/** + * 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 get_array() & noexcept; + simdjson_inline simdjson_result get_object() & noexcept; + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result get_value() noexcept; - simdjson_inline simdjson_result is_null() noexcept; - template simdjson_inline simdjson_result get() & noexcept; - simdjson_inline simdjson_result raw_json() noexcept; - simdjson_inline operator document& () const noexcept; + simdjson_inline simdjson_result is_null() noexcept; + template + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + template + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::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(*this).get(); + } + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_CONCEPTS + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { + static_assert(!sizeof(T), "The get 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 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, double, or bool."); + static_cast(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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " 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(out); // to get rid of unused errors + return UNINITIALIZED; +#endif // SIMDJSON_SUPPORTS_CONCEPTS + } + + /** @overload template error_code get(T &out) & noexcept */ + template simdjson_inline error_code get(T &out) && noexcept; + simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (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 - explicit simdjson_inline operator T() noexcept(false); - simdjson_inline operator array() & noexcept(false); - simdjson_inline operator 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 raw_json_string() noexcept(false); - simdjson_inline operator bool() noexcept(false); - simdjson_inline operator value() noexcept(false); + template + explicit simdjson_inline operator T() noexcept(false); + simdjson_inline operator array() & noexcept(false); + simdjson_inline operator 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 raw_json_string() noexcept(false); + simdjson_inline operator bool() noexcept(false); + simdjson_inline operator value() noexcept(false); #endif - simdjson_inline simdjson_result count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) & noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(const char* key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](const char* key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(const char* key) & noexcept; + simdjson_inline simdjson_result count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) & noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result end() & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(const char *key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; - simdjson_inline simdjson_result type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_string() noexcept; + simdjson_inline simdjson_result type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; - simdjson_inline simdjson_result current_location() noexcept; - simdjson_inline int32_t current_depth() const noexcept; - simdjson_inline bool is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - simdjson_inline simdjson_result raw_json_token() noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result current_location() noexcept; + simdjson_inline int32_t current_depth() const noexcept; + simdjson_inline bool is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + simdjson_inline simdjson_result raw_json_token() noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; - private: - document* doc{ nullptr }; - }; - } // namespace ondemand - } // namespace lsx +private: + document *doc{nullptr}; +}; +} // namespace ondemand +} // namespace lsx } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public lsx::implementation_simdjson_result_base { - 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; +template<> +struct simdjson_result : public lsx::implementation_simdjson_result_base { +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 get_array() & noexcept; - simdjson_inline simdjson_result get_object() & noexcept; - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result get_value() noexcept; - simdjson_inline simdjson_result is_null() noexcept; + simdjson_inline simdjson_result get_array() & noexcept; + simdjson_inline simdjson_result get_object() & noexcept; + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result get_value() noexcept; + simdjson_inline simdjson_result is_null() noexcept; - template simdjson_inline simdjson_result get() & noexcept; - template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept; + template simdjson_inline simdjson_result get() & noexcept; + template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept; - template simdjson_inline error_code get(T& out) & noexcept; - template simdjson_inline error_code get(T& out) && noexcept; + template simdjson_inline error_code get(T &out) & noexcept; + template simdjson_inline error_code get(T &out) && noexcept; #if SIMDJSON_EXCEPTIONS - template ::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); - 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); - simdjson_inline operator lsx::ondemand::value() noexcept(false); -#endif - simdjson_inline simdjson_result count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) & noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(const char* key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](const char* key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(const char* key) & noexcept; - simdjson_inline simdjson_result type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_string() noexcept; - simdjson_inline simdjson_result current_location() noexcept; - simdjson_inline int32_t current_depth() const noexcept; - simdjson_inline bool at_end() const noexcept; - simdjson_inline bool is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - /** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */ - simdjson_inline simdjson_result raw_json_token() noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - }; + using lsx::implementation_simdjson_result_base::operator*; + using lsx::implementation_simdjson_result_base::operator->; + template ::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); + 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); + simdjson_inline operator lsx::ondemand::value() noexcept(false); +#endif + simdjson_inline simdjson_result count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) & noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result end() & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(const char *key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + simdjson_inline simdjson_result type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + simdjson_inline simdjson_result current_location() noexcept; + simdjson_inline int32_t current_depth() const noexcept; + simdjson_inline bool at_end() const noexcept; + simdjson_inline bool is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + /** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */ + simdjson_inline simdjson_result raw_json_token() noexcept; + + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION +}; } // namespace simdjson @@ -101411,68 +146639,81 @@ namespace simdjson { namespace simdjson { - template<> - struct simdjson_result : public lsx::implementation_simdjson_result_base { - 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; +template<> +struct simdjson_result : public lsx::implementation_simdjson_result_base { +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 get_array() & noexcept; - simdjson_inline simdjson_result get_object() & noexcept; - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result get_value() noexcept; - simdjson_inline simdjson_result is_null() noexcept; + simdjson_inline simdjson_result get_array() & noexcept; + simdjson_inline simdjson_result get_object() & noexcept; + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result get_value() noexcept; + simdjson_inline simdjson_result is_null() noexcept; + + template simdjson_inline simdjson_result get() & noexcept; + template simdjson_inline simdjson_result get() && noexcept; + + template simdjson_inline error_code get(T &out) & noexcept; + template simdjson_inline error_code get(T &out) && noexcept; #if SIMDJSON_EXCEPTIONS - template ::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); - 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); - simdjson_inline operator lsx::ondemand::value() noexcept(false); + template + 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); + simdjson_inline operator lsx::ondemand::value() noexcept(false); #endif - simdjson_inline simdjson_result count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) & noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(const char* key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](const char* key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(const char* key) & noexcept; - simdjson_inline simdjson_result type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_string() noexcept; - simdjson_inline simdjson_result current_location() noexcept; - simdjson_inline simdjson_result current_depth() const noexcept; - simdjson_inline simdjson_result is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - /** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */ - simdjson_inline simdjson_result raw_json_token() noexcept; + simdjson_inline simdjson_result count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) & noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result end() & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(const char *key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + simdjson_inline simdjson_result type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + simdjson_inline simdjson_result current_location() noexcept; + simdjson_inline simdjson_result current_depth() const noexcept; + simdjson_inline simdjson_result is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + /** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */ + simdjson_inline simdjson_result raw_json_token() noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - }; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION +}; } // namespace simdjson @@ -101498,322 +146739,330 @@ namespace simdjson { #endif namespace simdjson { - namespace lsx { - namespace ondemand { +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 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: +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(); + /** + * 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 }; + 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{}; + /** + * We lock using a mutex. + */ + std::mutex locking_mutex{}; + std::condition_variable cond_var{}; - friend class document_stream; - }; + 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. - * - * ```c++ - * 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; +/** + * 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; + simdjson_inline ~document_stream() noexcept; - /** - * Returns the input size in bytes. - */ - inline size_t size_in_bytes() const 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(); - * - */ - inline size_t truncated_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(); + * + */ + inline size_t truncated_bytes() const noexcept; - class iterator { - public: - using value_type = simdjson_result; - using reference = simdjson_result; - using pointer = void; - using difference_type = std::ptrdiff_t; - using iterator_category = std::input_iterator_tag; + class iterator { + public: + using value_type = simdjson_result; + using reference = simdjson_result; + using pointer = void; + 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; + /** + * 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. - * - * 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 + * + * 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. + * + * 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 error of the stream (if any). + */ + inline error_code error() 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; + /** + * Returns whether the iterator is at the end. + */ + inline bool at_end() const noexcept; - friend class document; - friend class document_stream; - friend class json_iterator; - }; + 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; - /** - * 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; + friend class document; + friend class document_stream; + friend class json_iterator; + }; + using iterator = document_stream::iterator; - private: + /** + * 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; - document_stream& operator=(const document_stream&) = delete; // Disallow copying - document_stream(const document_stream& other) = delete; // Disallow copying +private: - /** - * 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) - */ - simdjson_inline document_stream( - ondemand::parser& parser, - const uint8_t* buf, - size_t len, - size_t batch_size, - bool allow_comma_separated - ) noexcept; + document_stream &operator=(const document_stream &) = delete; // Disallow copying + document_stream(const document_stream &other) = delete; // Disallow copying - /** - * Parse the first document in the buffer. Used by begin(), to handle allocation and - * initialization. - */ - inline void start() noexcept; + /** + * 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) + */ + simdjson_inline document_stream( + ondemand::parser &parser, + const uint8_t *buf, + size_t len, + size_t batch_size, + bool allow_comma_separated + ) 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; + /** + * Parse the first document in the buffer. Used by begin(), to handle allocation and + * initialization. + */ + inline void start() noexcept; - /** Move the json_iterator of the document to the location of the next document in the stream. */ - inline void next_document() 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; - /** Get the next document index. */ - inline size_t next_batch_start() const noexcept; + /** Move the json_iterator of the document to the location of the next document in the stream. */ + inline void next_document() 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; + /** Get the next document index. */ + inline size_t next_batch_start() const noexcept; - // Fields - ondemand::parser* parser; - const uint8_t* buf; - size_t len; - size_t batch_size; - bool allow_comma_separated; - /** - * 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{}; + /** Pass the next batch through stage 1 with the given parser. */ + inline error_code run_stage1(ondemand::parser &p, size_t batch_start) noexcept; -#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; + // Fields + ondemand::parser *parser; + const uint8_t *buf; + size_t len; + size_t batch_size; + bool allow_comma_separated; + /** + * 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{}; - inline void load_from_stage1_thread() noexcept; + #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; - /** Start a thread to run stage 1 on the next batch. */ - inline void start_stage1_thread() noexcept; + inline void load_from_stage1_thread() noexcept; - /** Wait for the stage 1 thread to finish and capture the results. */ - inline void finish_stage1_thread() noexcept; + /** Start a thread to run stage 1 on the next batch. */ + inline void start_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 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. - */ - ondemand::parser stage1_thread_parser{}; + /** Wait for the stage 1 thread to finish and capture the results. */ + inline void finish_stage1_thread() noexcept; - friend struct stage1_worker; -#endif // SIMDJSON_THREADS_ENABLED + /** 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 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. + */ + ondemand::parser stage1_thread_parser{}; - friend class parser; - friend class document; - friend class json_iterator; - friend struct simdjson_result; - friend struct simdjson::internal::simdjson_result_base; - }; // document_stream + friend struct stage1_worker; + #endif // SIMDJSON_THREADS_ENABLED - } // namespace ondemand - } // namespace lsx + friend class parser; + friend class document; + friend class json_iterator; + friend struct simdjson_result; + friend struct simdjson::internal::simdjson_result_base; +}; // document_stream + +} // namespace ondemand +} // namespace lsx } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public lsx::implementation_simdjson_result_base { - 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; - }; +template<> +struct simdjson_result : public lsx::implementation_simdjson_result_base { +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 @@ -101832,104 +147081,104 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace ondemand { +namespace lsx { +namespace ondemand { - /** - * A JSON field (key/value pair) in an object. - * - * Returned from object iteration. - * - * Extends from std::pair so you can use C++ algorithms that rely on pairs. - */ - class field : public std::pair { - public: - /** - * Create a new invalid field. - * - * Exists so you can declare a variable and later assign to it before use. - */ - simdjson_inline field() noexcept; +/** + * A JSON field (key/value pair) in an object. + * + * Returned from object iteration. + * + * Extends from std::pair so you can use C++ algorithms that rely on pairs. + */ +class field : public std::pair { +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 unescaped_key(bool allow_replacement = false) 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. 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 - 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; - /** - * Get the field value. - */ - simdjson_inline ondemand::value& value() & noexcept; - /** - * @overload ondemand::value &ondemand::value() & noexcept - */ - simdjson_inline ondemand::value value() && 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 unescaped_key(bool allow_replacement = false) 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. 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 + 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; + /** + * 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 start(value_iterator& parent_iter) noexcept; - static simdjson_inline simdjson_result start(const value_iterator& parent_iter, raw_json_string key) noexcept; - friend struct simdjson_result; - friend class object_iterator; - }; +protected: + simdjson_inline field(raw_json_string key, ondemand::value &&value) noexcept; + static simdjson_inline simdjson_result start(value_iterator &parent_iter) noexcept; + static simdjson_inline simdjson_result start(const value_iterator &parent_iter, raw_json_string key) noexcept; + friend struct simdjson_result; + friend class object_iterator; +}; - } // namespace ondemand - } // namespace lsx +} // namespace ondemand +} // namespace lsx } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public lsx::implementation_simdjson_result_base { - 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; +template<> +struct simdjson_result : public lsx::implementation_simdjson_result_base { +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 unescaped_key(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code unescaped_key(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result key() noexcept; - simdjson_inline simdjson_result key_raw_json_token() noexcept; - simdjson_inline simdjson_result escaped_key() noexcept; - simdjson_inline simdjson_result value() noexcept; - }; + simdjson_inline simdjson_result unescaped_key(bool allow_replacement = false) noexcept; + template + simdjson_inline error_code unescaped_key(string_type &receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result key() noexcept; + simdjson_inline simdjson_result key_raw_json_token() noexcept; + simdjson_inline simdjson_result escaped_key() noexcept; + simdjson_inline simdjson_result value() noexcept; +}; } // namespace simdjson @@ -101944,253 +147193,367 @@ namespace simdjson { /* 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 */ +/* 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 { +namespace lsx { +namespace ondemand { - /** - * 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; +/** + * 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; - simdjson_inline simdjson_result begin() noexcept; - simdjson_inline simdjson_result end() noexcept; - /** - * Look up a field by name on an object (order-sensitive). - * - * 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 }`: - * - * ```c++ - * 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 }`. - * - * 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 find_field(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result find_field(std::string_view key) && noexcept; + /** + * 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 + */ + simdjson_inline simdjson_result begin() noexcept; + simdjson_inline simdjson_result 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"); + * ``` + * 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 find_field(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result 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. - * - * It is the default, however, 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 find_field_unordered(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](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 find_field_unordered(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) && 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("/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 at_pointer(std::string_view json_pointer) 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("/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 at_pointer(std::string_view json_pointer) noexcept; - /** - * Get the value associated with the given JSONPath expression. We only support - * JSONPath queries that trivially convertible to JSON Pointer queries: key - * names and array indices. - * - * @return The value associated with the given JSONPath expression, or: - * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - */ - inline simdjson_result at_path(std::string_view json_path) noexcept; + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + inline simdjson_result at_path(std::string_view json_path) noexcept; - /** - * Reset the iterator so that we are pointing back at the - * beginning of the object. You should still consume values only once even if you - * 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 reset() & 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 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 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 raw_json() noexcept; + /** + * Get all values matching the given JSONPath expression with wildcard support. + * Supports wildcard patterns like ".*" to match all object fields. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern + */ + inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; - protected: - /** - * Go to the end of the object, no matter where you are right now. - */ - simdjson_inline error_code consume() noexcept; - static simdjson_inline simdjson_result start(value_iterator& iter) noexcept; - static simdjson_inline simdjson_result start_root(value_iterator& iter) noexcept; - static simdjson_inline simdjson_result started(value_iterator& iter) noexcept; - static simdjson_inline object resume(const value_iterator& iter) noexcept; - simdjson_inline object(const value_iterator& iter) 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 reset() & 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 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 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 raw_json() noexcept; - simdjson_warn_unused simdjson_inline error_code find_field_raw(const std::string_view key) 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + 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 + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } - value_iterator iter{}; +#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 + requires(std::is_class_v && (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 start(value_iterator &iter) noexcept; + static simdjson_inline simdjson_result start_root(value_iterator &iter) noexcept; + static simdjson_inline simdjson_result started(value_iterator &iter) noexcept; + static simdjson_inline object resume(const value_iterator &iter) noexcept; + simdjson_inline object(const value_iterator &iter) noexcept; - friend class value; - friend class document; - friend struct simdjson_result; - }; + simdjson_warn_unused simdjson_inline error_code find_field_raw(const std::string_view key) noexcept; - } // namespace ondemand - } // namespace lsx + value_iterator iter{}; + + friend class value; + friend class document; + friend struct simdjson_result; +}; + +} // namespace ondemand +} // namespace lsx } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public lsx::implementation_simdjson_result_base { - 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; +template<> +struct simdjson_result : public lsx::implementation_simdjson_result_base { +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 begin() noexcept; - simdjson_inline simdjson_result end() noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) && noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result begin() noexcept; + simdjson_inline simdjson_result end() noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) && noexcept; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + inline simdjson_result reset() noexcept; + inline simdjson_result is_empty() noexcept; + inline simdjson_result count_fields() & noexcept; + inline simdjson_result raw_json() noexcept; + #if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h - inline simdjson_result reset() noexcept; - inline simdjson_result is_empty() noexcept; - inline simdjson_result count_fields() & noexcept; - inline simdjson_result raw_json() noexcept; + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(out) ); + } + return SUCCESS; + } - }; +#if SIMDJSON_STATIC_REFLECTION + // TODO: move this code into object-inl.h + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) noexcept { + if (error()) { return error(); } + return first.extract_into(out); + } +#endif // SIMDJSON_STATIC_REFLECTION +#endif // SIMDJSON_SUPPORTS_CONCEPTS +}; } // namespace simdjson @@ -102208,72 +147571,77 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace ondemand { +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; +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; - // - // Iterator interface - // + // + // Iterator interface + // - // Reads key and value, yielding them to the user. - // MUST ONLY BE CALLED ONCE PER ITERATION. - simdjson_inline simdjson_result 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 ',' - simdjson_inline object_iterator& operator++() noexcept; + // Reads key and value, yielding them to the user. + // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline simdjson_result 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: - /** - * 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{}; +private: +#if SIMDJSON_DEVELOPMENT_CHECKS + bool has_been_referenced{false}; +#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; - friend class object; - }; + simdjson_inline object_iterator(const value_iterator &iter) noexcept; + friend struct simdjson_result; + friend class object; +}; - } // namespace ondemand - } // namespace lsx +} // namespace ondemand +} // namespace lsx } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public lsx::implementation_simdjson_result_base { - 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; +template<> +struct simdjson_result : public lsx::implementation_simdjson_result_base { +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 - // + // + // Iterator interface + // - // Reads key and value, yielding them to the user. - simdjson_inline simdjson_result 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&) const noexcept; - // Assumes it's being compared with the end. true if depth >= iter->depth. - simdjson_inline bool operator!=(const simdjson_result&) const noexcept; - // Checks for ']' and ',' - simdjson_inline simdjson_result& operator++() noexcept; - }; + // Reads key and value, yielding them to the user. + simdjson_inline simdjson_result 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 &) const noexcept; + // Assumes it's being compared with the end. true if depth >= iter->depth. + simdjson_inline bool operator!=(const simdjson_result &) const noexcept; + // Checks for ']' and ',' + simdjson_inline simdjson_result &operator++() noexcept; +}; } // namespace simdjson @@ -102289,35 +147657,49 @@ namespace simdjson { /* 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 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 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 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 to_json_string(lsx::ondemand::array& x) noexcept; - inline simdjson_result to_json_string(simdjson_result x); - inline simdjson_result to_json_string(simdjson_result x); - inline simdjson_result to_json_string(simdjson_result x); - inline simdjson_result to_json_string(simdjson_result x); +/** + * 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 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 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 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 to_json_string(lsx::ondemand::array& x) noexcept; +inline simdjson_result to_json_string(simdjson_result x); +inline simdjson_result to_json_string(simdjson_result x); +inline simdjson_result to_json_string(simdjson_result x); +inline simdjson_result to_json_string(simdjson_result 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 + requires(!std::same_as && + !std::same_as && + !std::same_as && + !std::same_as) +inline std::string to_json_string(const T& obj); +#endif + } // namespace simdjson /** @@ -102327,69 +147709,652 @@ namespace simdjson { * Credit: @madhur4127 * See https://github.com/simdjson/simdjson/issues/1768 */ -namespace simdjson { - namespace lsx { - namespace ondemand { +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); +/** + * 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 x); +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result 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); +/** + * 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 x); +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result 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); +/** + * 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&& x); +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& x); #endif - inline std::ostream& operator<<(std::ostream& out, simdjson::lsx::ondemand::document_reference& value); +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&& x); +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& 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); +/** + * 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 x); +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x); #endif - } - } -} // namespace simdjson::lsx::ondemand +}}} // 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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +#include +#if SIMDJSON_STATIC_REFLECTION +#include +// #include // for std::define_static_string - header not available yet +#endif + +namespace simdjson { + +////////////////////////////// +// Number deserialization +////////////////////////////// + +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + uint64_t x; + SIMDJSON_TRY(val.get_uint64().get(x)); + if (x > (limits::max)()) { + return NUMBER_OUT_OF_RANGE; + } + out = static_cast(x); + return SUCCESS; +} + +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + double x; + SIMDJSON_TRY(val.get_double().get(x)); + out = static_cast(x); + return SUCCESS; +} + +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + 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(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 +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v) { + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + out = T{str}; + return SUCCESS; +} + + +/** + * 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>(). + */ +template +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { + using value_type = typename std::remove_cvref_t::value_type; + static_assert( + deserializable, + "The specified type inside the container must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the container must default constructible."); + lsx::ondemand::array arr; + if constexpr (std::is_same_v, lsx::ondemand::array>) { + arr = val; + } else { + SIMDJSON_TRY(val.get_array().get(arr)); + } + + for (auto v : arr) { + if constexpr (concepts::returns_reference) { + if (auto const err = v.get().get(concepts::emplace_one(out)); + err) { + // If an error occurs, the empty element that we just inserted gets + // removed. We're not using a temp variable because if T is a heavy + // type, we want the valid path to be the fast path and the slow path be + // the path that has errors in it. + if constexpr (requires { out.pop_back(); }) { + static_cast(out.pop_back()); + } + return err; + } + } else { + value_type temp; + if (auto const err = v.get().get(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 +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { + using value_type = typename std::remove_cvref_t::mapped_type; + static_assert( + deserializable, + "The specified value type inside the container must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "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().get(this_value)); + [[maybe_unused]] std::pair 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 +error_code tag_invoke(deserialize_tag, lsx::ondemand::object &obj, T &out) noexcept { + using value_type = typename std::remove_cvref_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 +error_code tag_invoke(deserialize_tag, lsx::ondemand::value &val, T &out) noexcept { + lsx::ondemand::object obj; + SIMDJSON_TRY(val.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} + +template +error_code tag_invoke(deserialize_tag, lsx::ondemand::document &doc, T &out) noexcept { + lsx::ondemand::object obj; + SIMDJSON_TRY(doc.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} + +template +error_code tag_invoke(deserialize_tag, lsx::ondemand::document_reference &doc, T &out) noexcept { + 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. + * + * If constructing T is nothrow, this conversion should be nothrow as well since + * we return MEMALLOC if we're not able to allocate memory instead of throwing + * the error message. + * + * @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 +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::element_type, ValT>) { + using element_type = typename std::remove_cvref_t::element_type; + + // For better error messages, don't use these as constraints on + // the tag_invoke CPO. + static_assert( + deserializable, + "The specified type inside the unique_ptr must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the unique_ptr must default constructible."); + + auto ptr = new (std::nothrow) element_type(); + if (ptr == nullptr) { + return MEMALLOC; + } + SIMDJSON_TRY(val.template get(*ptr)); + out.reset(ptr); + return SUCCESS; +} + +/** + * This CPO (Customization Point Object) will help deserialize into optional types. + */ +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable::value_type, decltype(val)>) { + using value_type = typename std::remove_cvref_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(out.value())); + return SUCCESS; +} + + +#if SIMDJSON_STATIC_REFLECTION + + +template +constexpr bool user_defined_type = (std::is_class_v +&& !std::is_same_v && !std::is_same_v && !concepts::optional_type && +!concepts::appendable_containers); + + +template + requires(user_defined_type && std::is_class_v) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { + lsx::ondemand::object obj; + if constexpr (std::is_same_v, lsx::ondemand::object>) { + obj = val; + } else { + SIMDJSON_TRY(val.get_object().get(obj)); + } + 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)); + if constexpr (concepts::optional_type) { + // for optional members, it's ok if the key is missing + auto error = obj[key].get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + if(error == NO_SUCH_FIELD) { + out.[:mem:].reset(); + continue; + } + return error; + } + } else { + // for non-optional members, the key must be present + SIMDJSON_TRY(obj[key].get(out.[:mem:])); + } + } + }; + return simdjson::SUCCESS; +} + +// Support for enum deserialization - deserialize from string representation using expand approach from P2996R12 +template + requires(std::is_enum_v) +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)); + constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T)); + template for (constexpr auto enum_val : enumerators) { + if (str == std::meta::identifier_of(enum_val)) { + out = [:enum_val:]; + return SUCCESS; + } + }; + + return INCORRECT_TYPE; +#else + // Fallback: deserialize as integer if reflection not available + std::underlying_type_t int_val; + SIMDJSON_TRY(val.get(int_val)); + out = static_cast(int_val); + return SUCCESS; +#endif +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr &out) noexcept { + if (!out) { + out = std::make_unique(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr &out) noexcept { + if (!out) { + out = std::make_shared(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +#endif // SIMDJSON_STATIC_REFLECTION + +//////////////////////////////////////// +// Unique pointers +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + 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 &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + 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 &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_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 &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_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 &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(); + } + int64_t temp; + SIMDJSON_TRY(val.get_int64().get(temp)); + *out = static_cast(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 */ @@ -102397,6 +148362,7 @@ namespace simdjson { /* 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" */ @@ -102406,279 +148372,291 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace ondemand { +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. - // +// +// ### 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 array::array(const value_iterator &_iter) noexcept + : iter{_iter} +{ +} - simdjson_inline simdjson_result 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::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::started(value_iterator& iter) noexcept { - bool has_value; - SIMDJSON_TRY(iter.started_array().get(has_value)); - return array(iter); - } +simdjson_inline simdjson_result 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::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::started(value_iterator &iter) noexcept { + bool has_value; + SIMDJSON_TRY(iter.started_array().get(has_value)); + return array(iter); +} - simdjson_inline simdjson_result array::begin() noexcept { +simdjson_inline simdjson_result array::begin() noexcept { #if SIMDJSON_DEVELOPMENT_CHECKS - if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } + if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } #endif - return array_iterator(iter); - } - simdjson_inline simdjson_result array::end() noexcept { - return array_iterator(iter); - } - simdjson_inline error_code array::consume() noexcept { - auto error = iter.json_iter().skip_child(iter.depth() - 1); - if (error) { iter.abandon(); } - return error; - } + return array_iterator(iter); +} +simdjson_inline simdjson_result 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 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(starting_point), size_t(final_point - starting_point)); - } +simdjson_inline simdjson_result 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(starting_point), size_t(final_point - starting_point)); +} - SIMDJSON_PUSH_DISABLE_WARNINGS - SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING - simdjson_inline simdjson_result array::count_elements() & 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 array after counting the number of elements. - iter.reset_array(); - return count; - } - SIMDJSON_POP_DISABLE_WARNINGS +SIMDJSON_PUSH_DISABLE_WARNINGS +SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING +simdjson_inline simdjson_result array::count_elements() & 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 array after counting the number of elements. + iter.reset_array(); + return count; +} +SIMDJSON_POP_DISABLE_WARNINGS - simdjson_inline simdjson_result array::is_empty() & noexcept { - bool is_not_empty; - auto error = iter.reset_array().get(is_not_empty); - if (error) { return error; } - return !is_not_empty; - } +simdjson_inline simdjson_result array::is_empty() & noexcept { + bool is_not_empty; + auto error = iter.reset_array().get(is_not_empty); + if(error) { return error; } + return !is_not_empty; +} - inline simdjson_result array::reset() & noexcept { - return iter.reset_array(); - } +inline simdjson_result array::reset() & noexcept { + return iter.reset_array(); +} - inline simdjson_result array::at_pointer(std::string_view json_pointer) noexcept { - if (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; } +inline simdjson_result array::at_pointer(std::string_view json_pointer) noexcept { + if (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; } - // Read the array index - size_t array_index = 0; - size_t i; - for (i = 0; i < json_pointer.length() && json_pointer[i] != '/'; i++) { - uint8_t digit = uint8_t(json_pointer[i] - '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; } - array_index = array_index * 10 + digit; - } + // Read the array index + size_t array_index = 0; + size_t i; + for (i = 0; i < json_pointer.length() && json_pointer[i] != '/'; i++) { + uint8_t digit = uint8_t(json_pointer[i] - '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; } + array_index = array_index*10 + digit; + } - // 0 followed by other digits is invalid - if (i > 1 && json_pointer[0] == '0') { return INVALID_JSON_POINTER; } // "JSON pointer array index has other characters after 0" + // 0 followed by other digits is invalid + if (i > 1 && json_pointer[0] == '0') { return INVALID_JSON_POINTER; } // "JSON pointer array index has other characters after 0" - // Empty string is invalid; so is a "/" with no digits before it - if (i == 0) { return INVALID_JSON_POINTER; } // "Empty string in JSON pointer array index" - // Get the child - auto child = at(array_index); - // If there is an error, it ends here - if (child.error()) { - return child; - } + // Empty string is invalid; so is a "/" with no digits before it + if (i == 0) { return INVALID_JSON_POINTER; } // "Empty string in JSON pointer array index" + // 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; - } + // 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 std::string json_path_to_pointer_conversion(std::string_view json_path) { - if (json_path.empty() || (json_path.front() != '.' && - json_path.front() != '[')) { - return "-1"; // This is just a sentinel value, the caller should check for this and return an error. - } +inline simdjson_result array::at_path(std::string_view json_path) noexcept { + auto json_pointer = json_path_to_pointer_conversion(json_path); + if (json_pointer == "-1") { return INVALID_JSON_POINTER; } + return at_pointer(json_pointer); +} - std::string result; - // Reserve space to reduce allocations, adjusting for potential increases due - // to escaping. - result.reserve(json_path.size() * 2); +inline simdjson_result> array::at_path_with_wildcard(std::string_view json_path) noexcept { + std::vector result; - size_t i = 0; + 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; + // Wildcard case + if(key=="*"){ + for(auto element: *this){ - 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; - } + if(element.error()){ + return element.error(); + } - return result; - } + if(remaining_path.empty()){ + // Use value_unsafe() because we've already checked for errors above. + // The 'element' is a simdjson_result wrapper, and we need to extract + // the underlying value. value_unsafe() is safe here because error() returned false. + result.push_back(std::move(element).value_unsafe()); - inline simdjson_result array::at_path(std::string_view json_path) noexcept { - auto json_pointer = json_path_to_pointer_conversion(json_path); - if (json_pointer == "-1") { return INVALID_JSON_POINTER; } - return at_pointer(json_pointer); - } + }else{ + auto nested_result = element.at_path_with_wildcard(remaining_path); - simdjson_inline simdjson_result array::at(size_t index) noexcept { - size_t i = 0; - for (auto value : *this) { - if (i == index) { return value; } - i++; - } - return INDEX_OUT_OF_BOUNDS; - } + if(nested_result.error()){ + return nested_result.error(); + } + // Same logic as above. + std::vector nested_matches = std::move(nested_result).value_unsafe(); - } // namespace ondemand - } // namespace lsx + result.insert(result.end(), + std::make_move_iterator(nested_matches.begin()), + std::make_move_iterator(nested_matches.end())); + } + } + return result; + }else{ + // Specific index case in which we access the element at the given index + size_t idx=0; + + for(char c:key){ + if(c < '0' || c > '9'){ + return INVALID_JSON_POINTER; + } + idx = idx*10 + (c - '0'); + } + + auto element = at(idx); + + if(element.error()){ + return element.error(); + } + + if(remaining_path.empty()){ + result.push_back(std::move(element).value_unsafe()); + return result; + }else{ + return element.at_path_with_wildcard(remaining_path); + } + } +} + +simdjson_inline simdjson_result array::at(size_t index) noexcept { + 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::simdjson_result( - lsx::ondemand::array&& value - ) noexcept - : implementation_simdjson_result_base( - std::forward(value) - ) - { - } - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept - : implementation_simdjson_result_base(error) - { - } +simdjson_inline simdjson_result::simdjson_result( + lsx::ondemand::array &&value +) noexcept + : implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept + : implementation_simdjson_result_base(error) +{ +} - simdjson_inline simdjson_result simdjson_result::begin() noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() noexcept { - if (error()) { return error(); } - return first.end(); - } - simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); - } - simdjson_inline simdjson_result simdjson_result::is_empty() & noexcept { - if (error()) { return error(); } - return first.is_empty(); - } - simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } - simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { return error(); } - return first.at_path(json_path); - } - simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { - if (error()) { return error(); } - return first.raw_json(); - } +simdjson_inline simdjson_result simdjson_result::begin() noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() noexcept { + if (error()) { return error(); } + return first.end(); +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::is_empty() & noexcept { + if (error()) { return error(); } + return first.is_empty(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path(json_path); +} +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path_with_wildcard(json_path); +} +simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { + if (error()) { return error(); } + return first.raw_json(); +} } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H @@ -102696,74 +148674,661 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace ondemand { +namespace lsx { +namespace ondemand { - simdjson_inline array_iterator::array_iterator(const value_iterator& _iter) noexcept - : iter{ _iter } - {} +simdjson_inline array_iterator::array_iterator(const value_iterator &_iter) noexcept + : iter{_iter} +{} - simdjson_inline simdjson_result array_iterator::operator*() noexcept { - 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 { - 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 simdjson_result 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; +} - } // namespace ondemand - } // namespace lsx +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::simdjson_result( - lsx::ondemand::array_iterator&& value - ) noexcept - : lsx::implementation_simdjson_result_base(std::forward(value)) - { - first.iter.assert_is_valid(); - } - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : lsx::implementation_simdjson_result_base({}, error) - { - } - - simdjson_inline simdjson_result simdjson_result::operator*() noexcept { - if (error()) { return error(); } - return *first; - } - simdjson_inline bool simdjson_result::operator==(const simdjson_result& other) const noexcept { - if (!first.iter.is_valid()) { return !error(); } - return first == other.first; - } - simdjson_inline bool simdjson_result::operator!=(const simdjson_result& other) const noexcept { - if (!first.iter.is_valid()) { return error(); } - return first != other.first; - } - simdjson_inline simdjson_result& simdjson_result::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 simdjson_result::simdjson_result( + lsx::ondemand::array_iterator &&value +) noexcept + : lsx::implementation_simdjson_result_base(std::forward(value)) +{ + first.iter.assert_is_valid(); +} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : lsx::implementation_simdjson_result_base({}, error) +{ +} +simdjson_inline simdjson_result simdjson_result::operator*() noexcept { + if (error()) { return error(); } + return *first; +} +simdjson_inline bool simdjson_result::operator==(const simdjson_result &other) const noexcept { + if (!first.iter.is_valid()) { return !error(); } + return first == other.first; +} +simdjson_inline bool simdjson_result::operator!=(const simdjson_result &other) const noexcept { + if (!first.iter.is_valid()) { return error(); } + return first != other.first; +} +simdjson_inline simdjson_result &simdjson_result::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::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 */ + +namespace simdjson { +namespace lsx { +namespace ondemand { + +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 value::get_array() noexcept { + return array::start(iter); +} +simdjson_inline simdjson_result value::get_object() noexcept { + return object::start(iter); +} +simdjson_inline simdjson_result value::start_or_resume_object() noexcept { + if (iter.at_start()) { + return get_object(); + } else { + return object::resume(iter); + } +} + +simdjson_inline simdjson_result value::get_raw_json_string() noexcept { + return iter.get_raw_json_string(); +} +simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { + return iter.get_string(allow_replacement); +} +template +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 value::get_wobbly_string() noexcept { + return iter.get_wobbly_string(); +} +simdjson_inline simdjson_result value::get_double() noexcept { + return iter.get_double(); +} +simdjson_inline simdjson_result value::get_double_in_string() noexcept { + return iter.get_double_in_string(); +} +simdjson_inline simdjson_result value::get_uint64() noexcept { + return iter.get_uint64(); +} +simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { + return iter.get_uint64_in_string(); +} +simdjson_inline simdjson_result value::get_int64() noexcept { + return iter.get_int64(); +} +simdjson_inline simdjson_result value::get_int64_in_string() noexcept { + return iter.get_int64_in_string(); +} +simdjson_inline simdjson_result value::get_bool() noexcept { + return iter.get_bool(); +} +simdjson_inline simdjson_result value::is_null() noexcept { + return iter.is_null(); +} + +template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result 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); } +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(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(bool& out) noexcept { return get_bool().get(out); } + +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline value::operator T() noexcept(false) { + return get(); +} +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 value::begin() & noexcept { + return get_array().begin(); +} +simdjson_inline simdjson_result value::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result value::count_elements() & noexcept { + simdjson_result 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 value::count_fields() & noexcept { + simdjson_result answer; + auto a = get_object(); + answer = a.count_fields(); + iter.move_at_start(); + return answer; +} +simdjson_inline simdjson_result value::at(size_t index) noexcept { + auto a = get_array(); + return a.at(index); +} + +simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result value::find_field(const char *key) noexcept { + return start_or_resume_object().find_field(key); +} + +simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result value::find_field_unordered(const char *key) noexcept { + return start_or_resume_object().find_field_unordered(key); +} + +simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { + return start_or_resume_object()[key]; +} +simdjson_inline simdjson_result value::operator[](const char *key) noexcept { + return start_or_resume_object()[key]; +} + +simdjson_inline simdjson_result value::type() noexcept { + return iter.type(); +} + +simdjson_inline simdjson_result 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 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 value::is_integer() noexcept { + return iter.is_integer(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { + return iter.get_number_type(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { + return iter.get_number(); +} + +simdjson_inline std::string_view value::raw_json_token() noexcept { + return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); +} + +simdjson_inline simdjson_result 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 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::at_pointer(std::string_view json_pointer) noexcept { + 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::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; + } +} + +inline simdjson_result> value::at_path_with_wildcard(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_with_wildcard(json_path); + case json_type::object: + return (*this).get_object().at_path_with_wildcard(json_path); + default: + return INVALID_JSON_POINTER; + } +} + +} // namespace ondemand +} // namespace lsx +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + lsx::ondemand::value &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base(error) +{ +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + if (error()) { return error(); } + return {}; +} + +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) noexcept { + if (error()) { return error(); } + return first.find_field(key); +} + +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} + +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) noexcept { + if (error()) { return error(); } + return first[key]; +} + +simdjson_inline simdjson_result simdjson_result::get_array() noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} + +template<> simdjson_inline error_code simdjson_result::get(lsx::ondemand::value &out) noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} + +template simdjson_inline simdjson_result simdjson_result::get() noexcept { + if (error()) { return error(); } + return first.get(); +} +template simdjson_inline error_code simdjson_result::get(T &out) noexcept { + if (error()) { return error(); } + return first.get(out); +} + +template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { + if (error()) { return error(); } + return std::move(first); +} + +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline simdjson_result::operator T() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first.get(); +} +simdjson_inline simdjson_result::operator lsx::ondemand::array() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator lsx::ondemand::object() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator lsx::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { + if (error()) { return error(); } + return first.raw_json(); +} + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { + if (error()) { return error(); } + return first.current_depth(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer( + std::string_view json_pointer) noexcept { + if (error()) { + return error(); + } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + +inline simdjson_result> simdjson_result::at_path_with_wildcard( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path_with_wildcard(json_path); +} + +} // 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 @@ -102776,911 +149341,1055 @@ namespace simdjson { /* 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 */ namespace simdjson { - namespace lsx { - namespace ondemand { +namespace lsx { +namespace ondemand { - simdjson_inline document::document(ondemand::json_iterator&& _iter) noexcept - : iter{ std::forward(_iter) } - { - logger::log_start_value(iter, "document"); - } +simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept + : iter{std::forward(_iter)} +{ + logger::log_start_value(iter, "document"); +} - simdjson_inline document document::start(json_iterator&& iter) noexcept { - return document(std::forward(iter)); - } +simdjson_inline document document::start(json_iterator &&iter) noexcept { + return document(std::forward(iter)); +} - inline void document::rewind() noexcept { - iter.rewind(); - } +inline void document::rewind() noexcept { + iter.rewind(); +} - inline std::string document::to_debug_string() noexcept { - return iter.to_string(); - } +inline std::string document::to_debug_string() noexcept { + return iter.to_string(); +} - inline simdjson_result document::current_location() const noexcept { - return iter.current_location(); - } +inline simdjson_result document::current_location() const noexcept { + return iter.current_location(); +} - inline int32_t document::current_depth() const noexcept { - return iter.depth(); - } +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::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 document::start_or_resume_object() noexcept { - if (iter.at_root()) { - return get_object(); - } - else { - return object::resume(resume_value_iterator()); - } - } - simdjson_inline simdjson_result document::get_value() noexcept { - // Make sure we start any arrays or objects before returning, so that start_root_() - // gets called. +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 document::start_or_resume_object() noexcept { + if (iter.at_root()) { + return get_object(); + } else { + return object::resume(resume_value_iterator()); + } +} +simdjson_inline simdjson_result document::get_value() noexcept { + // Make sure we start any arrays or objects before returning, so that start_root_() + // 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. + // 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; } + 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 document::get_array() & noexcept { - auto value = get_root_value_iterator(); - return array::start_root(value); - } - simdjson_inline simdjson_result document::get_object() & noexcept { - auto value = get_root_value_iterator(); - return object::start_root(value); - } + // 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 document::get_array() & noexcept { + auto value = get_root_value_iterator(); + return array::start_root(value); +} +simdjson_inline simdjson_result 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. - */ +/** + * 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 document::get_uint64() noexcept { - return get_root_value_iterator().get_root_uint64(true); - } - simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { - return get_root_value_iterator().get_root_uint64_in_string(true); - } - simdjson_inline simdjson_result document::get_int64() noexcept { - return get_root_value_iterator().get_root_int64(true); - } - simdjson_inline simdjson_result document::get_int64_in_string() noexcept { - return get_root_value_iterator().get_root_int64_in_string(true); - } - simdjson_inline simdjson_result document::get_double() noexcept { - return get_root_value_iterator().get_root_double(true); - } - simdjson_inline simdjson_result document::get_double_in_string() noexcept { - return get_root_value_iterator().get_root_double_in_string(true); - } - simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { - return get_root_value_iterator().get_root_string(true, allow_replacement); - } - template - 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 document::get_wobbly_string() noexcept { - return get_root_value_iterator().get_root_wobbly_string(true); - } - simdjson_inline simdjson_result document::get_raw_json_string() noexcept { - return get_root_value_iterator().get_root_raw_json_string(true); - } - simdjson_inline simdjson_result document::get_bool() noexcept { - return get_root_value_iterator().get_root_bool(true); - } - simdjson_inline simdjson_result document::is_null() noexcept { - return get_root_value_iterator().is_root_null(true); - } +simdjson_inline simdjson_result document::get_uint64() noexcept { + return get_root_value_iterator().get_root_uint64(true); +} +simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { + return get_root_value_iterator().get_root_uint64_in_string(true); +} +simdjson_inline simdjson_result document::get_int64() noexcept { + return get_root_value_iterator().get_root_int64(true); +} +simdjson_inline simdjson_result document::get_int64_in_string() noexcept { + return get_root_value_iterator().get_root_int64_in_string(true); +} +simdjson_inline simdjson_result document::get_double() noexcept { + return get_root_value_iterator().get_root_double(true); +} +simdjson_inline simdjson_result document::get_double_in_string() noexcept { + return get_root_value_iterator().get_root_double_in_string(true); +} +simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { + return get_root_value_iterator().get_root_string(true, allow_replacement); +} +template +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 document::get_wobbly_string() noexcept { + return get_root_value_iterator().get_root_wobbly_string(true); +} +simdjson_inline simdjson_result document::get_raw_json_string() noexcept { + return get_root_value_iterator().get_root_raw_json_string(true); +} +simdjson_inline simdjson_result document::get_bool() noexcept { + return get_root_value_iterator().get_root_bool(true); +} +simdjson_inline simdjson_result document::is_null() noexcept { + return get_root_value_iterator().is_root_null(true); +} - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } - template<> simdjson_deprecated simdjson_inline simdjson_result 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); } +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(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(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_inline error_code document::get(T& out) & noexcept { - return get().get(out); - } - template simdjson_deprecated simdjson_inline error_code document::get(T& out) && noexcept { - return std::forward(*this).get().get(out); - } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_value(); } #if SIMDJSON_EXCEPTIONS - template - simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } - template - simdjson_inline document::operator T() & noexcept(false) { return get(); } - 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) { return get_string(false); } - simdjson_inline document::operator raw_json_string() noexcept(false) { 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(); } +template +simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } +template +simdjson_inline document::operator T() & noexcept(false) { return get(); } +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 document::count_elements() & noexcept { - auto a = get_array(); - simdjson_result 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 document::count_elements() & noexcept { + auto a = get_array(); + simdjson_result 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 document::count_fields() & noexcept { + auto a = get_object(); + simdjson_result 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 document::at(size_t index) & noexcept { + auto a = get_array(); + return a.at(index); +} +simdjson_inline simdjson_result document::begin() & noexcept { + return get_array().begin(); +} +simdjson_inline simdjson_result document::end() & noexcept { + return {}; +} + +simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field(const char *key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::find_field_unordered(const char *key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { + return start_or_resume_object()[key]; +} +simdjson_inline simdjson_result 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 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(starting_point), size_t(final_point - starting_point)); +} + +simdjson_inline simdjson_result document::type() noexcept { + return get_root_value_iterator().type(); +} + +simdjson_inline simdjson_result 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 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 document::is_integer() noexcept { + return get_root_value_iterator().is_root_integer(true); +} + +simdjson_inline simdjson_result document::get_number_type() noexcept { + return get_root_value_iterator().get_root_number_type(true); +} + +simdjson_inline simdjson_result document::get_number() noexcept { + return get_root_value_iterator().get_root_number(true); +} + + +simdjson_inline simdjson_result document::raw_json_token() noexcept { + auto _iter = get_root_value_iterator(); + return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); +} + +simdjson_inline simdjson_result 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 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; + } +} + +simdjson_inline simdjson_result> document::at_path_with_wildcard(std::string_view json_path) noexcept { + rewind(); // Rewind the document each time 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().at_path_with_wildcard(json_path); + case json_type::object: + return (*this).get_object().at_path_with_wildcard(json_path); + default: + return INVALID_JSON_POINTER; + } +} + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (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) { + // 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; } - simdjson_inline simdjson_result document::count_fields() & noexcept { - auto a = get_object(); - simdjson_result 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 document::at(size_t index) & noexcept { - auto a = get_array(); - return a.at(index); - } - simdjson_inline simdjson_result document::begin() & noexcept { - return get_array().begin(); - } - simdjson_inline simdjson_result document::end() & noexcept { - return {}; - } - - simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { - return start_or_resume_object().find_field(key); - } - simdjson_inline simdjson_result document::find_field(const char* key) & noexcept { - return start_or_resume_object().find_field(key); - } - simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { - return start_or_resume_object().find_field_unordered(key); - } - simdjson_inline simdjson_result document::find_field_unordered(const char* key) & noexcept { - return start_or_resume_object().find_field_unordered(key); - } - simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { - return start_or_resume_object()[key]; - } - simdjson_inline simdjson_result document::operator[](const char* key) & noexcept { - return start_or_resume_object()[key]; - } - - simdjson_inline error_code document::consume() noexcept { - auto error = iter.skip_child(0); - if (error) { iter.abandon(); } - return error; - } - - simdjson_inline simdjson_result 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(starting_point), size_t(final_point - starting_point)); - } - - simdjson_inline simdjson_result document::type() noexcept { - return get_root_value_iterator().type(); - } - - simdjson_inline simdjson_result document::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 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 document::is_integer() noexcept { - return get_root_value_iterator().is_root_integer(true); - } - - simdjson_inline simdjson_result document::get_number_type() noexcept { - return get_root_value_iterator().get_root_number_type(true); - } - - simdjson_inline simdjson_result document::get_number() noexcept { - return get_root_value_iterator().get_root_number(true); - } - - - simdjson_inline simdjson_result document::raw_json_token() noexcept { - auto _iter = get_root_value_iterator(); - return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); - } - - simdjson_inline simdjson_result 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 document::at_path(std::string_view json_path) noexcept { - rewind(); // Rewind the document each time at_pointer is called - if (json_path.empty()) { - return this->get_value(); - } - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) { - case json_type::array: - return (*this).get_array().at_path(json_path); - case json_type::object: - return (*this).get_object().at_path(json_path); - default: - return INVALID_JSON_POINTER; - } - } - - } // namespace ondemand - } // namespace lsx -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result( - lsx::ondemand::document&& value - ) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) - { - } - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept : - implementation_simdjson_result_base( - error - ) - { - } - simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); - } - simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); - } - simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; - } - simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() & noexcept { - return {}; - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char* key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](const char* key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::find_field(const char* key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { - if (error()) { return error(); } - return first.get_array(); - } - simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { - if (error()) { return error(); } - return first.get_object(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); - } - simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); - } - simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); - } - template - simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(receiver, allow_replacement); - } - simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); - } - simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); - } - simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); - } - simdjson_inline simdjson_result simdjson_result::get_value() noexcept { - if (error()) { return error(); } - return first.get_value(); - } - simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); - } - - template - simdjson_inline simdjson_result simdjson_result::get() & noexcept { - if (error()) { return error(); } - return first.get(); - } - template - simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { - if (error()) { return error(); } - return std::forward(first).get(); - } - template - simdjson_inline error_code simdjson_result::get(T& out) & noexcept { - if (error()) { return error(); } - return first.get(out); - } - template - simdjson_inline error_code simdjson_result::get(T& out) && noexcept { - if (error()) { return error(); } - return std::forward(first).get(out); - } - - template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; - template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { - if (error()) { return error(); } - return std::forward(first); - } - template<> simdjson_inline error_code simdjson_result::get(lsx::ondemand::document& out) & noexcept = delete; - template<> simdjson_inline error_code simdjson_result::get(lsx::ondemand::document& out) && noexcept { - if (error()) { return error(); } - out = std::forward(first); - return SUCCESS; - } - - simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); - } - - simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); - } - - simdjson_inline simdjson_result simdjson_result::is_string() noexcept { - if (error()) { return error(); } - return first.is_string(); - } - - simdjson_inline bool simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); - } - - simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); - } - - simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); - } - - simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); - } - - -#if SIMDJSON_EXCEPTIONS - template ::value == false>::type> - simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator lsx::ondemand::array() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator lsx::ondemand::object() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator lsx::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator lsx::ondemand::value() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } -#endif - - - simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); - } - - simdjson_inline bool simdjson_result::at_end() const noexcept { - if (error()) { return error(); } - return first.at_end(); - } - - - simdjson_inline int32_t simdjson_result::current_depth() const noexcept { - if (error()) { return error(); } - return first.current_depth(); - } - - simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); - } - - simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } - - simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { return error(); } - return first.at_path(json_path); - } - -} // namespace simdjson - - -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 document_reference::get_array() & noexcept { return doc->get_array(); } - simdjson_inline simdjson_result 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 document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } - simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } - simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } - simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } - simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } - simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } - simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } - template - 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 document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } - simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } - simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } - simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } - simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } -#if SIMDJSON_EXCEPTIONS - template - simdjson_inline document_reference::operator T() noexcept(false) { return get(); } - 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 document_reference::count_elements() & noexcept { return doc->count_elements(); } - simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } - simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } - simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } - simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } - simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } - simdjson_inline simdjson_result document_reference::find_field(const char* key) & noexcept { return doc->find_field(key); } - simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } - simdjson_inline simdjson_result document_reference::operator[](const char* key) & noexcept { return (*doc)[key]; } - simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } - simdjson_inline simdjson_result document_reference::find_field_unordered(const char* key) & noexcept { return doc->find_field_unordered(key); } - simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } - simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } - simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } - simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } - simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } - simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } - simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } - simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } - simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } - simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } - simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } - simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } - simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json(); } - simdjson_inline document_reference::operator document& () const noexcept { return *doc; } - - } // namespace ondemand - } // namespace lsx -} // namespace simdjson - - - -namespace simdjson { - simdjson_inline simdjson_result::simdjson_result(lsx::ondemand::document_reference value, error_code error) - noexcept : implementation_simdjson_result_base(std::forward(value), error) {} - - - simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); - } - simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); - } - simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; - } - simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() & noexcept { - return {}; - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char* key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](const char* key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::find_field(const char* key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { - if (error()) { return error(); } - return first.get_array(); - } - simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { - if (error()) { return error(); } - return first.get_object(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); - } - simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); - } - simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); - } - template - simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(receiver, allow_replacement); - } - simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); - } - simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); - } - simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); - } - simdjson_inline simdjson_result simdjson_result::get_value() noexcept { - if (error()) { return error(); } - return first.get_value(); - } - simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); - } - simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); - } - simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); - } - simdjson_inline simdjson_result simdjson_result::is_string() noexcept { - if (error()) { return error(); } - return first.is_string(); - } - simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); - } - simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); - } - simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); - } - simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); - } -#if SIMDJSON_EXCEPTIONS - template ::value == false>::type> - simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator lsx::ondemand::array() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator lsx::ondemand::object() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator lsx::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator lsx::ondemand::value() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } -#endif - - simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); - } - - simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); - } - - simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } - - simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { - return error(); + } 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 first.at_path(json_path); + } } + }; + return SUCCESS; +} + +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +} // namespace ondemand +} // namespace lsx +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + lsx::ondemand::document &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base( + error + ) +{ +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { + if (error()) { return error(); } + return first.get_value(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} + +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} + +template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; +template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first); +} +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(lsx::ondemand::document &out) & noexcept = delete; +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(lsx::ondemand::document &out) && noexcept { + if (error()) { return error(); } + out = std::forward(first); + return SUCCESS; +} + +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} + +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} + +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} + +simdjson_inline bool simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} + +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} + +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} + +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} + + +#if SIMDJSON_EXCEPTIONS +template ::value == false>::type> +simdjson_inline simdjson_result::operator T() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator lsx::ondemand::array() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator lsx::ondemand::object() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator lsx::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator lsx::ondemand::value() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline bool simdjson_result::at_end() const noexcept { + if (error()) { return error(); } + return first.at_end(); +} + + +simdjson_inline int32_t simdjson_result::current_depth() const noexcept { + if (error()) { return error(); } + return first.current_depth(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path(json_path); +} + +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path_with_wildcard(json_path); +} + +#if SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(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 document_reference::get_array() & noexcept { return doc->get_array(); } +simdjson_inline simdjson_result 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 document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } +simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } +simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } +template +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 document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } +simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } +simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } +simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } +simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline document_reference::operator T() noexcept(false) { return get(); } +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 document_reference::count_elements() & noexcept { return doc->count_elements(); } +simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } +simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } +simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } +simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } +simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } +simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } +simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } +simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } +simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } +simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } +simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } +simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } +simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } +simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } +simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } +simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } +simdjson_inline simdjson_result> document_reference::at_path_with_wildcard(std::string_view json_path) noexcept { return doc->at_path_with_wildcard(json_path); } +simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} +simdjson_inline document_reference::operator document&() const noexcept { return *doc; } +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code document_reference::extract_into(T& out) & noexcept { + return doc->extract_into(out); +} +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION +} // namespace ondemand +} // namespace lsx +} // namespace simdjson + + + +namespace simdjson { +simdjson_inline simdjson_result::simdjson_result(lsx::ondemand::document_reference value, error_code error) + noexcept : implementation_simdjson_result_base(std::forward(value), error) {} + + +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { + if (error()) { return error(); } + return first.get_value(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} +template <> +simdjson_warn_unused simdjson_inline error_code simdjson_result::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::get(lsx::ondemand::document_reference &out) && noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline simdjson_result::operator T() noexcept(false) { + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + if (error()) { throw simdjson_error(error()); } + return first.get(); +} +simdjson_inline simdjson_result::operator lsx::ondemand::array() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator lsx::ondemand::object() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator lsx::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator lsx::ondemand::value() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path_with_wildcard(json_path); +} +#if SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(out); +} +#endif // SIMDJSON_STATIC_REFLECTION } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H @@ -103701,423 +150410,432 @@ namespace simdjson { #include namespace simdjson { - namespace lsx { - namespace ondemand { +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 lock(locking_mutex); - cond_var.wait(lock, [this] {return has_work == false; }); - } +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 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 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 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 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::start_thread() { + std::unique_lock 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 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 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::stop_thread() { + std::unique_lock 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 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(); - } +inline void stage1_worker::run(document_stream * ds, parser * stage1, size_t next_batch_start) { + std::unique_lock 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 - ) 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 }, - error{ SUCCESS } +simdjson_inline document_stream::document_stream( + ondemand::parser &_parser, + const uint8_t *_buf, + size_t _len, + size_t _batch_size, + bool _allow_comma_separated +) 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}, + 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 - , use_thread(_parser.threaded) // we need to make a copy because _parser.threaded can change + if(worker.get() == nullptr) { + error = MEMALLOC; + } #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 }, - allow_comma_separated{ false }, - error{ UNINITIALIZED } -#ifdef SIMDJSON_THREADS_ENABLED - , use_thread(false) -#endif - { - } +simdjson_inline document_stream::document_stream() noexcept + : parser{nullptr}, + buf{nullptr}, + len{0}, + batch_size{0}, + allow_comma_separated{false}, + 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::~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::size_in_bytes() const noexcept { + return len; +} - inline size_t document_stream::truncated_bytes() const noexcept { - 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::truncated_bytes() const noexcept { + 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() 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 document_stream::iterator::iterator(document_stream* _stream, bool is_end) noexcept + : stream{_stream}, finished{is_end} { +} - simdjson_inline simdjson_result document_stream::iterator::operator*() noexcept { - //if(stream->error) { return stream->error; } - return simdjson_result(stream->doc, stream->error); - } +simdjson_inline simdjson_result document_stream::iterator::operator*() noexcept { + return simdjson_result(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 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; - } +simdjson_inline bool document_stream::iterator::at_end() const noexcept { + return 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); - } +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->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; } - doc_index = batch_start; - doc = document(json_iterator(&buf[batch_start], parser)); - doc.iter._streaming = true; +simdjson_inline bool document_stream::iterator::operator==(const document_stream::iterator &other) const noexcept { + return finished == other.finished; +} -#ifdef SIMDJSON_THREADS_ENABLED - if (use_thread && next_batch_start() < len) { - // Kick off the first thread on next batch if needed - error = stage1_thread_parser.allocate(batch_size); - if (error) { return; } - worker->start_thread(); - start_stage1_thread(); - if (error) { return; } - } -#endif // SIMDJSON_THREADS_ENABLED - } +simdjson_inline document_stream::iterator document_stream::begin() noexcept { + start(); + // If there are no documents, we're finished. + return iterator(this, error == EMPTY); +} - 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]; +simdjson_inline document_stream::iterator document_stream::end() noexcept { + return iterator(this, true); +} - // Check if at end of structural indexes (i.e. at end of batch) - if (cur_struct_index >= static_cast(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. - */ +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; } + doc_index = batch_start; + doc = document(json_iterator(&buf[batch_start], parser)); + doc.iter._streaming = true; - if (error) { continue; } // If the error was EMPTY, we may want to load another batch. - doc_index = batch_start; - } - } - } + #ifdef SIMDJSON_THREADS_ENABLED + if (use_thread && next_batch_start() < len) { + // Kick off the first thread on next batch if needed + 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_document() noexcept { - // 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) { doc.iter.consume_character(','); } - // 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 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]; - inline size_t document_stream::next_batch_start() const noexcept { - return batch_start + parser->implementation->structural_indexes[parser->implementation->n_structural_indexes]; - } + // Check if at end of structural indexes (i.e. at end of batch) + if(cur_struct_index >= static_cast(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. + */ - 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; - if (remaining <= batch_size) { - return p.implementation->stage1(&buf[_batch_start], remaining, stage1_mode::streaming_final); - } - else { - return p.implementation->stage1(&buf[_batch_start], batch_size, stage1_mode::streaming_partial); - } - } + if (error) { continue; } // If the error was EMPTY, we may want to load another batch. + doc_index = batch_start; + } + } +} - simdjson_inline size_t document_stream::iterator::current_index() const noexcept { - return stream->doc_index; - } +inline void document_stream::next_document() noexcept { + // 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(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(); +} - simdjson_inline std::string_view document_stream::iterator::source() const noexcept { - auto depth = stream->doc.iter.depth(); - auto cur_struct_index = stream->doc.iter._root - stream->parser->implementation->structural_indexes.get(); +inline size_t document_stream::next_batch_start() const noexcept { + return batch_start + parser->implementation->structural_indexes[parser->implementation->n_structural_indexes]; +} - // If at root, process the first token to determine if scalar value - if (stream->doc.iter.at_root()) { - 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 - // TODO: We could remove trailing whitespaces - // This returns a string spanning from start of value to the beginning of the next document (excluded) - { - auto next_index = 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(stream->buf) + current_index(); - while (svlen > 1 && (std::isspace(start[svlen - 1]) || start[svlen - 1] == '\0')) { - svlen--; - } - return std::string_view(start, svlen); - } - } - cur_struct_index++; - } +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; + if (remaining <= batch_size) { + return p.implementation->stage1(&buf[_batch_start], remaining, stage1_mode::streaming_final); + } else { + return p.implementation->stage1(&buf[_batch_start], batch_size, stage1_mode::streaming_partial); + } +} - while (cur_struct_index <= static_cast(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++; - } +simdjson_inline size_t document_stream::iterator::current_index() const noexcept { + return stream->doc_index; +} - return std::string_view(reinterpret_cast(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[cur_struct_index] - current_index() + stream->batch_start + 1);; - } +simdjson_inline std::string_view document_stream::iterator::source() const noexcept { + auto depth = stream->doc.iter.depth(); + auto cur_struct_index = stream->doc.iter._root - stream->parser->implementation->structural_indexes.get(); - inline error_code document_stream::iterator::error() const noexcept { - return stream->error; - } + // If at root, process the first token to determine if scalar value + if (stream->doc.iter.at_root()) { + 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 + // TODO: We could remove trailing whitespaces + // This returns a string spanning from start of value to the beginning of the next document (excluded) + { + auto next_index = 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(stream->buf) + current_index(); + while(svlen > 1 && (std::isspace(start[svlen-1]) || start[svlen-1] == '\0')) { + svlen--; + } + return std::string_view(start, svlen); + } + } + cur_struct_index++; + } + + while (cur_struct_index <= static_cast(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(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; } +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(); - } - } + // 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(); +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); - } + worker->run(this, & this->stage1_thread_parser, _next_batch_start); +} #endif // SIMDJSON_THREADS_ENABLED - } // namespace ondemand - } // namespace lsx +} // namespace ondemand +} // namespace lsx } // namespace simdjson namespace simdjson { - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept : - implementation_simdjson_result_base(error) - { - } - simdjson_inline simdjson_result::simdjson_result( - lsx::ondemand::document_stream&& value - ) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) - { - } +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base(error) +{ +} +simdjson_inline simdjson_result::simdjson_result( + lsx::ondemand::document_stream &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} } @@ -104136,120 +150854,120 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace ondemand { +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() {} +// clang 6 does not think the default constructor can be noexcept, so we make it explicit +simdjson_inline field::field() noexcept : std::pair() {} - simdjson_inline field::field(raw_json_string key, ondemand::value&& value) noexcept - : std::pair(key, std::forward(value)) - { - } +simdjson_inline field::field(raw_json_string key, ondemand::value &&value) noexcept + : std::pair(key, std::forward(value)) +{ +} - simdjson_inline simdjson_result 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::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::start(const value_iterator& parent_iter, raw_json_string key) noexcept { - return field(key, parent_iter.child()); - } +simdjson_inline simdjson_result 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 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 answer = first.unescape(second.iter.json_iter(), allow_replacement); - first.consume(); - return answer; - } +simdjson_inline simdjson_warn_unused simdjson_result 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 answer = first.unescape(second.iter.json_iter(), allow_replacement); + first.consume(); + return answer; +} - template - 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)); - receiver = key; - return SUCCESS; - } +template +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) ); + 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 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(first.buf - 1), second.iter._json_iter->token.peek(-1) - first.buf + 1); - } +simdjson_inline std::string_view field::key_raw_json_token() const noexcept { + SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us. + return std::string_view(reinterpret_cast(first.buf-1), second.iter._json_iter->token.peek(-1) - first.buf + 1); +} - simdjson_inline 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(first.buf), end_quote - first.buf); - } +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(first.buf), end_quote - first.buf); +} - simdjson_inline value& field::value() & noexcept { - return second; - } +simdjson_inline value &field::value() & noexcept { + return second; +} - simdjson_inline value field::value() && noexcept { - return std::forward(*this).second; - } +simdjson_inline value field::value() && noexcept { + return std::forward(*this).second; +} - } // namespace ondemand - } // namespace lsx +} // namespace ondemand +} // namespace lsx } // namespace simdjson namespace simdjson { - simdjson_inline simdjson_result::simdjson_result( - lsx::ondemand::field&& value - ) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) - { - } - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept : - implementation_simdjson_result_base(error) - { - } +simdjson_inline simdjson_result::simdjson_result( + lsx::ondemand::field &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base(error) +{ +} - simdjson_inline simdjson_result simdjson_result::key() noexcept { - if (error()) { return error(); } - return first.key(); - } +simdjson_inline simdjson_result simdjson_result::key() noexcept { + if (error()) { return error(); } + return first.key(); +} - simdjson_inline simdjson_result simdjson_result::key_raw_json_token() noexcept { - if (error()) { return error(); } - return first.key_raw_json_token(); - } +simdjson_inline simdjson_result simdjson_result::key_raw_json_token() noexcept { + if (error()) { return error(); } + return first.key_raw_json_token(); +} - simdjson_inline simdjson_result simdjson_result::escaped_key() noexcept { - if (error()) { return error(); } - return first.escaped_key(); - } +simdjson_inline simdjson_result simdjson_result::escaped_key() noexcept { + if (error()) { return error(); } + return first.escaped_key(); +} - simdjson_inline simdjson_result simdjson_result::unescaped_key(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.unescaped_key(allow_replacement); - } +simdjson_inline simdjson_result simdjson_result::unescaped_key(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.unescaped_key(allow_replacement); +} - template - simdjson_inline error_code simdjson_result::unescaped_key(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.unescaped_key(receiver, allow_replacement); - } +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::unescaped_key(string_type &receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.unescaped_key(receiver, allow_replacement); +} - simdjson_inline simdjson_result simdjson_result::value() noexcept { - if (error()) { return error(); } - return std::move(first.value()); - } +simdjson_inline simdjson_result simdjson_result::value() noexcept { + if (error()) { return error(); } + return std::move(first.value()); +} } // namespace simdjson @@ -104272,432 +150990,440 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace ondemand { +namespace lsx { +namespace ondemand { - simdjson_inline json_iterator::json_iterator(json_iterator&& other) noexcept - : token(std::forward(other.token)), - parser{ other.parser }, - _string_buf_loc{ other._string_buf_loc }, - error{ other.error }, - _depth{ other._depth }, - _root{ other._root }, - _streaming{ other._streaming } - { - 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; - other.parser = nullptr; - return *this; - } +simdjson_inline json_iterator::json_iterator(json_iterator &&other) noexcept + : token(std::forward(other.token)), + parser{other.parser}, + _string_buf_loc{other._string_buf_loc}, + error{other.error}, + _depth{other._depth}, + _root{other._root}, + _streaming{other._streaming} +{ + 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; + 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 } +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(); +{ + logger::log_headers(); #if SIMDJSON_CHECK_EOF - assert_more_tokens(); + 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 } +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} - { - logger::log_headers(); +{ + logger::log_headers(); #if SIMDJSON_CHECK_EOF - assert_more_tokens(); + 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 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; - } +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 +// 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; } + // 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"); } + // 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 '"': - if (*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; - } + break; + case '"': + if(*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; - } - } + // 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(TAPE_ERROR, "not enough close braces"); - } + return report_error(TAPE_ERROR, "not enough close braces"); +} - SIMDJSON_POP_DISABLE_WARNINGS +SIMDJSON_POP_DISABLE_WARNINGS - simdjson_inline bool json_iterator::at_root() const noexcept { - return position() == root_position(); - } +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::is_single_token() const noexcept { + return parser->implementation->n_structural_indexes == 1; +} - simdjson_inline bool json_iterator::streaming() const noexcept { - return _streaming; - } +simdjson_inline bool json_iterator::streaming() const noexcept { + return _streaming; +} - simdjson_inline token_position json_iterator::root_position() const noexcept { - return _root; - } +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_document_depth() const noexcept { + SIMDJSON_ASSUME( _depth == 1 ); +} - simdjson_inline void json_iterator::assert_at_root() 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); + // 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_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 { +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]); + 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]; - } +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(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 std::string json_iterator::to_string() const noexcept { + if( !is_alive() ) { return "dead json_iterator instance"; } + const char * current_structural = reinterpret_cast(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 json_iterator::current_location() const noexcept { - if (!is_alive()) { // Unrecoverable error - if (!at_root()) { - return reinterpret_cast(token.peek(-1)); - } - else { - return reinterpret_cast(token.peek()); - } - } - if (at_end()) { - return OUT_OF_BOUNDS; - } - return reinterpret_cast(token.peek()); - } +inline simdjson_result json_iterator::current_location() const noexcept { + if (!is_alive()) { // Unrecoverable error + if (!at_root()) { + return reinterpret_cast(token.peek(-1)); + } else { + return reinterpret_cast(token.peek()); + } + } + if (at_end()) { + return OUT_OF_BOUNDS; + } + return reinterpret_cast(token.peek()); +} - simdjson_inline bool json_iterator::is_alive() const noexcept { - return parser; - } +simdjson_inline bool json_iterator::is_alive() const noexcept { + return parser; +} - simdjson_inline void json_iterator::abandon() noexcept { - parser = nullptr; - _depth = 0; - } +simdjson_inline void json_iterator::abandon() noexcept { + parser = nullptr; + _depth = 0; +} - simdjson_inline const uint8_t* json_iterator::return_current_and_advance() noexcept { +simdjson_inline const uint8_t *json_iterator::return_current_and_advance() noexcept { #if SIMDJSON_CHECK_EOF - assert_more_tokens(); + assert_more_tokens(); #endif // SIMDJSON_CHECK_EOF - return token.return_current_and_advance(); - } + 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::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 { +simdjson_inline const uint8_t *json_iterator::peek(int32_t delta) const noexcept { #if SIMDJSON_CHECK_EOF - assert_more_tokens(delta + 1); + assert_more_tokens(delta+1); #endif // SIMDJSON_CHECK_EOF - return token.peek(delta); - } + return token.peek(delta); +} - simdjson_inline uint32_t json_iterator::peek_length(int32_t delta) const noexcept { +simdjson_inline uint32_t json_iterator::peek_length(int32_t delta) const noexcept { #if SIMDJSON_CHECK_EOF - assert_more_tokens(delta + 1); + assert_more_tokens(delta+1); #endif // #if SIMDJSON_CHECK_EOF - return token.peek_length(delta); - } + 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 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 { +simdjson_inline uint32_t json_iterator::peek_length(token_position position) const noexcept { #if SIMDJSON_CHECK_EOF - assert_valid_position(position); + 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 { + 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); + assert_valid_position(position); #endif // SIMDJSON_CHECK_EOF - return token.peek_root_length(position); - } + 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 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::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 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 depth_t json_iterator::depth() const noexcept { + return _depth; +} - simdjson_inline uint8_t*& json_iterator::string_buf_loc() noexcept { - return _string_buf_loc; - } +simdjson_inline uint8_t *&json_iterator::string_buf_loc() noexcept { + return _string_buf_loc; +} - 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_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 token_position json_iterator::position() const noexcept { + return token.position(); +} - simdjson_inline simdjson_result json_iterator::unescape(raw_json_string in, bool allow_replacement) noexcept { +simdjson_inline simdjson_result 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); - SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc)); - return result; + 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); + return parser->unescape(in, _string_buf_loc, allow_replacement); #endif - } +} - simdjson_inline simdjson_result json_iterator::unescape_wobbly(raw_json_string in) noexcept { +simdjson_inline simdjson_result json_iterator::unescape_wobbly(raw_json_string in) noexcept { #if SIMDJSON_DEVELOPMENT_CHECKS - auto result = parser->unescape_wobbly(in, _string_buf_loc); - SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc)); - return result; + 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); + 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); +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]); + 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; - } + token.set_position(position); + _depth = child_depth; +} - simdjson_inline error_code json_iterator::consume_character(char c) noexcept { - if (*peek() == c) { - return_current_and_advance(); - return SUCCESS; - } - return TAPE_ERROR; - } +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 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; } - } +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_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 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; - } +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 ondemand +} // namespace lsx } // namespace simdjson namespace simdjson { - simdjson_inline simdjson_result::simdjson_result(lsx::ondemand::json_iterator&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} +simdjson_inline simdjson_result::simdjson_result(lsx::ondemand::json_iterator &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(error) {} } // namespace simdjson @@ -104715,109 +151441,109 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace ondemand { +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; - default: SIMDJSON_UNREACHABLE(); - } - return out; - } +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; + default: SIMDJSON_UNREACHABLE(); + } + return out; +} #if SIMDJSON_EXCEPTIONS - inline std::ostream& operator<<(std::ostream& out, simdjson_result& type) noexcept(false) { - return out << type.value(); - } +inline std::ostream& operator<<(std::ostream& out, simdjson_result &type) noexcept(false) { + return out << type.value(); +} #endif - simdjson_inline number_type number::get_number_type() const noexcept { - return type; - } +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 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 uint64_t number::get_uint64() const noexcept { + return payload.unsigned_integer; +} - simdjson_inline number::operator uint64_t() const noexcept { - return get_uint64(); - } +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 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 int64_t number::get_int64() const noexcept { + return payload.signed_integer; +} - simdjson_inline number::operator int64_t() const noexcept { - return get_int64(); - } +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 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 double number::get_double() const noexcept { + return payload.floating_point_number; +} - simdjson_inline number::operator double() const noexcept { - return get_double(); - } +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 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_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_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::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; - } +simdjson_inline void number::skip_double() noexcept { + type = number_type::floating_point_number; +} - } // namespace ondemand - } // namespace lsx +} // namespace ondemand +} // namespace lsx } // namespace simdjson namespace simdjson { - simdjson_inline simdjson_result::simdjson_result(lsx::ondemand::json_type&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} +simdjson_inline simdjson_result::simdjson_result(lsx::ondemand::json_type &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(error) {} } // namespace simdjson @@ -104839,216 +151565,214 @@ namespace simdjson { #include namespace simdjson { - namespace lsx { - namespace ondemand { - namespace logger { +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. +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 ' '; - } - } +// Helper to turn unprintable or newline characters into spaces +static inline char printable_char(char c) { + if (c >= 0x20) { + return c; + } else { + return ' '; + } +} - template - 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_s); - if (size <= 0) return std::string(); - std::unique_ptr 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); - } +template +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_s); + if (size <= 0) return std::string(); + std::unique_ptr 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 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 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(); - } +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_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_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_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_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_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_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_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_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_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_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"); +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); - } - } - } + 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 - 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)...); - } +template +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)...); +} - template - 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)...); - 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); - } - } - } +template +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)...); + 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 logger +} // namespace ondemand +} // namespace lsx } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_LOGGER_INL_H @@ -105066,269 +151790,369 @@ namespace simdjson { /* 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): #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 */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace ondemand { +namespace lsx { +namespace ondemand { - simdjson_inline simdjson_result 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(key.size()), key.data()); - return NO_SUCH_FIELD; - } - return value(iter.child()); - } - simdjson_inline simdjson_result 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(key.size()), key.data()); - return NO_SUCH_FIELD; - } - return value(iter.child()); - } - simdjson_inline simdjson_result object::operator[](const std::string_view key) & noexcept { - return find_field_unordered(key); - } - simdjson_inline simdjson_result object::operator[](const std::string_view key) && noexcept { - return std::forward(*this).find_field_unordered(key); - } - simdjson_inline simdjson_result object::find_field(const std::string_view key) & noexcept { - 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(key.size()), key.data()); - return NO_SUCH_FIELD; - } - return value(iter.child()); - } - simdjson_inline simdjson_result object::find_field(const std::string_view key) && noexcept { - 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(key.size()), key.data()); - return NO_SUCH_FIELD; - } - return value(iter.child()); - } +simdjson_inline simdjson_result 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} +simdjson_inline simdjson_result 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} +simdjson_inline simdjson_result object::operator[](const std::string_view key) & noexcept { + return find_field_unordered(key); +} +simdjson_inline simdjson_result object::operator[](const std::string_view key) && noexcept { + return std::forward(*this).find_field_unordered(key); +} +simdjson_inline simdjson_result object::find_field(const std::string_view key) & noexcept { + 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} +simdjson_inline simdjson_result object::find_field(const std::string_view key) && noexcept { + 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} - simdjson_inline simdjson_result object::start(value_iterator& iter) noexcept { - SIMDJSON_TRY(iter.start_object().error()); - return object(iter); - } - simdjson_inline simdjson_result object::start_root(value_iterator& iter) noexcept { - SIMDJSON_TRY(iter.start_root_object().error()); - return object(iter); - } - 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 object::start(value_iterator &iter) noexcept { + SIMDJSON_TRY( iter.start_object().error() ); + return object(iter); +} +simdjson_inline simdjson_result 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 object::raw_json() noexcept { - 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(starting_point), size_t(final_point - starting_point)); - } +simdjson_inline simdjson_result object::raw_json() noexcept { + 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(starting_point), size_t(final_point - starting_point)); +} - simdjson_inline simdjson_result object::started(value_iterator& iter) noexcept { - SIMDJSON_TRY(iter.started_object().error()); - return object(iter); - } +simdjson_inline simdjson_result 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::resume(const value_iterator &iter) noexcept { + return iter; +} - simdjson_inline object::object(const value_iterator& _iter) noexcept - : iter{ _iter } - { - } +simdjson_inline object::object(const value_iterator &_iter) noexcept + : iter{_iter} +{ +} - simdjson_inline simdjson_result object::begin() noexcept { +simdjson_inline simdjson_result object::begin() noexcept { #if SIMDJSON_DEVELOPMENT_CHECKS - if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } + if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } #endif - return object_iterator(iter); - } - simdjson_inline simdjson_result object::end() noexcept { - return object_iterator(iter); - } + return object_iterator(iter); +} +simdjson_inline simdjson_result object::end() noexcept { + return object_iterator(iter); +} - inline simdjson_result object::at_pointer(std::string_view json_pointer) noexcept { - if (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 child; +inline simdjson_result object::at_pointer(std::string_view json_pointer) noexcept { + if (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 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; + // 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 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); +} + +inline simdjson_result> object::at_path_with_wildcard(std::string_view json_path) noexcept { + std::vector result; + + 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; + // 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()) { + result.push_back(std::move(val)); + } else { + auto nested_result = val.at_path_with_wildcard(remaining_path); + + if (nested_result.error()) { + return nested_result.error(); + } + // Extract and append all nested matches to our result + std::vector nested_vec; + SIMDJSON_TRY(std::move(nested_result).get(nested_vec)); + + result.insert(result.end(), + std::make_move_iterator(nested_vec.begin()), + std::make_move_iterator(nested_vec.end())); + } + } + return result; + } else { + value val; + SIMDJSON_TRY(find_field(key).get(val)); + + if (remaining_path.empty()) { + result.push_back(std::move(val)); + return result; + } else { + return val.at_path_with_wildcard(remaining_path); + } + } +} + +simdjson_inline simdjson_result 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 object::is_empty() & noexcept { + 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 object::reset() & noexcept { + return iter.reset_object(); +} + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (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) { + // 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:])); + } + } + } + }; - inline simdjson_result object::at_path(std::string_view json_path) noexcept { - auto json_pointer = json_path_to_pointer_conversion(json_path); - if (json_pointer == "-1") { - return INVALID_JSON_POINTER; - } - return at_pointer(json_pointer); - } + return SUCCESS; +} - simdjson_inline simdjson_result 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; - } +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION - simdjson_inline simdjson_result object::is_empty() & noexcept { - 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 object::reset() & noexcept { - return iter.reset_object(); - } - - } // namespace ondemand - } // namespace lsx +} // namespace ondemand +} // namespace lsx } // namespace simdjson namespace simdjson { - simdjson_inline simdjson_result::simdjson_result(lsx::ondemand::object&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} +simdjson_inline simdjson_result::simdjson_result(lsx::ondemand::object &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(error) {} - simdjson_inline simdjson_result simdjson_result::begin() noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() noexcept { - if (error()) { return error(); } - return first.end(); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) && noexcept { - if (error()) { return error(); } - return std::forward(first).find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) && noexcept { - if (error()) { return error(); } - return std::forward(first)[key]; - } - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) && noexcept { - if (error()) { return error(); } - return std::forward(first).find_field(key); - } +simdjson_inline simdjson_result simdjson_result::begin() noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() noexcept { + if (error()) { return error(); } + return first.end(); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) && noexcept { + if (error()) { return error(); } + return std::forward(first).find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) && noexcept { + if (error()) { return error(); } + return std::forward(first)[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) && noexcept { + if (error()) { return error(); } + return std::forward(first).find_field(key); +} - simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} - simdjson_inline simdjson_result simdjson_result::at_path( - std::string_view json_path) noexcept { - if (error()) { - return error(); - } - return first.at_path(json_path); - } +simdjson_inline simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} - inline simdjson_result simdjson_result::reset() noexcept { - if (error()) { return error(); } - return first.reset(); - } +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path_with_wildcard(json_path); +} - inline simdjson_result simdjson_result::is_empty() noexcept { - if (error()) { return error(); } - return first.is_empty(); - } +inline simdjson_result simdjson_result::reset() noexcept { + if (error()) { return error(); } + return first.reset(); +} - simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); - } +inline simdjson_result simdjson_result::is_empty() noexcept { + if (error()) { return error(); } + return first.is_empty(); +} + +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { + if (error()) { return error(); } + return first.raw_json(); +} - simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { - if (error()) { return error(); } - return first.raw_json(); - } } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H @@ -105346,129 +152170,139 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace ondemand { +namespace lsx { +namespace ondemand { - // - // object_iterator - // +// +// object_iterator +// - simdjson_inline object_iterator::object_iterator(const value_iterator& _iter) noexcept - : iter{ _iter } - {} +simdjson_inline object_iterator::object_iterator(const value_iterator &_iter) noexcept + : iter{_iter} +{} - simdjson_inline simdjson_result object_iterator::operator*() noexcept { - 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_inline simdjson_result 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 { - // 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_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 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 + 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. - // +// +// ### 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 ondemand +} // namespace lsx } // namespace simdjson namespace simdjson { - simdjson_inline simdjson_result::simdjson_result( - lsx::ondemand::object_iterator&& value - ) noexcept - : implementation_simdjson_result_base(std::forward(value)) - { - first.iter.assert_is_valid(); - } - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base({}, error) - { - } +simdjson_inline simdjson_result::simdjson_result( + lsx::ondemand::object_iterator &&value +) noexcept + : implementation_simdjson_result_base(std::forward(value)) +{ + first.iter.assert_is_valid(); +} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base({}, error) +{ +} - simdjson_inline simdjson_result simdjson_result::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::operator==(const simdjson_result& 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::operator!=(const simdjson_result& other) const noexcept { - if (!first.iter.is_valid()) { return error(); } - return first != other.first; - } - // Checks for ']' and ',' - simdjson_inline simdjson_result& simdjson_result::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 simdjson_result simdjson_result::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::operator==(const simdjson_result &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::operator!=(const simdjson_result &other) const noexcept { + if (!first.iter.is_valid()) { return error(); } + return first != other.first; +} +// Checks for ']' and ',' +simdjson_inline simdjson_result &simdjson_result::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 @@ -105492,193 +152326,225 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace ondemand { +namespace lsx { +namespace ondemand { - simdjson_inline parser::parser(size_t max_capacity) noexcept - : _max_capacity{ max_capacity } { - } +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; } +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; - size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * new_capacity / 3 + SIMDJSON_PADDING, 64); - string_buf.reset(new (std::nothrow) uint8_t[string_capacity]); + // string_capacity copied from document::allocate + _capacity = 0; + 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]); + 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 (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()); - } +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 parser::iterate(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(padded_string_view json) & noexcept { + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } - json.remove_utf8_bom(); + json.remove_utf8_bom(); - // Allocate if needed - if (capacity() < json.length() || !string_buf) { - SIMDJSON_TRY(allocate(json.length(), max_depth())); - } + // 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(json.data()), json.length(), stage1_mode::regular)); - return document::start({ reinterpret_cast(json.data()), this }); - } + // Run stage 1. + SIMDJSON_TRY( implementation->stage1(reinterpret_cast(json.data()), json.length(), stage1_mode::regular) ); + return document::start({ reinterpret_cast(json.data()), this }); +} #ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON - simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept { + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } - json.remove_utf8_bom(); + json.remove_utf8_bom(); - // Allocate if needed - if (capacity() < json.length() || !string_buf) { - SIMDJSON_TRY(allocate(json.length(), max_depth())); - } + // 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(json.data()), json.length(), stage1_mode::regular); - if (err) { - if (err != UNCLOSED_STRING) - return err; - } - return document::start({ reinterpret_cast(json.data()), this, true }); - } + // Run stage 1. + const simdjson::error_code err = implementation->stage1(reinterpret_cast(json.data()), json.length(), stage1_mode::regular); + if (err) { + if (err != UNCLOSED_STRING) + return err; + } + return document::start({ reinterpret_cast(json.data()), this, true }); +} #endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON - simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 parser::iterate(std::string_view json, size_t allocated) & noexcept { - return iterate(padded_string_view(json, allocated)); - } +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(std::string_view json, size_t allocated) & noexcept { + return iterate(padded_string_view(json, allocated)); +} - simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(std::string& json) & noexcept { - if (json.capacity() - json.size() < SIMDJSON_PADDING) { - json.reserve(json.size() + SIMDJSON_PADDING); - } - return iterate(padded_string_view(json)); - } +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(std::string &json) & noexcept { + return iterate(pad_with_reserve(json)); +} - simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const std::string& json) & noexcept { - return iterate(padded_string_view(json)); - } +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const std::string &json) & noexcept { + return iterate(padded_string_view(json)); +} - simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const simdjson_result& result) & noexcept { - // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 parser::iterate(const simdjson_result &result) & noexcept { + // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 parser::iterate(const simdjson_result& result) & noexcept { - // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 parser::iterate(const simdjson_result &result) & noexcept { + // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 parser::iterate_raw(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_raw(padded_string_view json) & noexcept { + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } - json.remove_utf8_bom(); + json.remove_utf8_bom(); - // Allocate if needed - if (capacity() < json.length()) { - SIMDJSON_TRY(allocate(json.length(), max_depth())); - } + // Allocate if needed + if (capacity() < json.length()) { + SIMDJSON_TRY( allocate(json.length(), max_depth()) ); + } - // Run stage 1. - SIMDJSON_TRY(implementation->stage1(reinterpret_cast(json.data()), json.length(), stage1_mode::regular)); - return json_iterator(reinterpret_cast(json.data()), this); - } + // Run stage 1. + SIMDJSON_TRY( implementation->stage1(reinterpret_cast(json.data()), json.length(), stage1_mode::regular) ); + return json_iterator(reinterpret_cast(json.data()), this); +} - inline simdjson_result parser::iterate_many(const uint8_t* buf, size_t len, size_t batch_size, bool allow_comma_separated) 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 (allow_comma_separated && batch_size < len) { batch_size = len; } - return document_stream(*this, buf, len, batch_size, allow_comma_separated); - } - inline simdjson_result parser::iterate_many(const char* buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept { - return iterate_many(reinterpret_cast(buf), len, batch_size, allow_comma_separated); - } - inline simdjson_result parser::iterate_many(const std::string& s, size_t batch_size, bool allow_comma_separated) noexcept { - return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated); - } - inline simdjson_result parser::iterate_many(const padded_string& s, size_t batch_size, bool allow_comma_separated) noexcept { - return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated); - } +inline simdjson_result 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; + } + if(allow_comma_separated && batch_size < len) { batch_size = len; } + return document_stream(*this, buf, len, batch_size, allow_comma_separated); +} - 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; - } +inline simdjson_result 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(buf), len, batch_size, allow_comma_separated); +} +inline simdjson_result 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 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 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 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_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 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 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(dst), end - dst); - dst = end; - return result; - } +simdjson_inline simdjson_warn_unused simdjson_result 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(dst), end-dst); + dst = end; + return result; +} - simdjson_inline simdjson_warn_unused simdjson_result 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(dst), end - dst); - dst = end; - return result; - } +simdjson_inline simdjson_warn_unused simdjson_result 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(dst), end-dst); + dst = end; + return result; +} - } // namespace ondemand - } // namespace lsx +simdjson_inline simdjson_warn_unused ondemand::parser& parser::get_parser() { + return *parser::get_parser_instance(); +} + +simdjson_inline bool 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& parser::get_parser_instance() { + std::unique_ptr& 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& 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 parser_instance = nullptr; + return parser_instance; +} + + +} // namespace ondemand +} // namespace lsx } // namespace simdjson namespace simdjson { - simdjson_inline simdjson_result::simdjson_result(lsx::ondemand::parser&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} +simdjson_inline simdjson_result::simdjson_result(lsx::ondemand::parser &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(error) {} } // namespace simdjson @@ -105698,456 +152564,210 @@ namespace simdjson { namespace simdjson { - namespace lsx { - namespace ondemand { +namespace lsx { +namespace ondemand { - simdjson_inline raw_json_string::raw_json_string(const uint8_t* _buf) noexcept : buf{ _buf } {} +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(buf); } +simdjson_inline const char * raw_json_string::raw() const noexcept { + return reinterpret_cast(buf); +} + +simdjson_inline char raw_json_string::operator[](size_t i) const noexcept { + return reinterpret_cast(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::is_free_from_unescaped_quote(std::string_view target) noexcept { - size_t pos{ 0 }; - // if the content has no escape character, just scan through it quickly! - for (; pos < target.size() && target[pos] != '\\'; pos++) {} - // slow path may begin. - bool escaping{ false }; - for (; pos < target.size(); pos++) { - if ((target[pos] == '"') && !escaping) { - return false; - } - else if (target[pos] == '\\') { - escaping = !escaping; - } - else { - escaping = false; - } - } - 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::is_free_from_unescaped_quote(const char* target) noexcept { - size_t pos{ 0 }; - // if the content has no escape character, just scan through it quickly! - for (; target[pos] && target[pos] != '\\'; pos++) {} - // slow path may begin. - bool escaping{ false }; - for (; target[pos]; pos++) { - if ((target[pos] == '"') && !escaping) { - return false; - } - else if (target[pos] == '\\') { - escaping = !escaping; - } - else { - escaping = false; - } - } - return true; - } +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()); + } + const char * r{raw()}; + size_t pos{0}; + for(;pos < target.size();pos++) { + if(r[pos] != target[pos]) { return false; } + } + if(r[pos] != '"') { return false; } + return true; +} + +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(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(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::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()); - } - const char* r{ raw() }; - size_t pos{ 0 }; - for (; pos < target.size(); 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_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_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 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 simdjson_warn_unused simdjson_result raw_json_string::unescape(json_iterator &iter, bool allow_replacement) const noexcept { + return iter.unescape(*this, allow_replacement); +} - 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_inline simdjson_warn_unused simdjson_result raw_json_string::unescape_wobbly(json_iterator &iter) const noexcept { + return iter.unescape_wobbly(*this); +} - 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 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++; + } +} - 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 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 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 ondemand +} // namespace lsx } // namespace simdjson namespace simdjson { - simdjson_inline simdjson_result::simdjson_result(lsx::ondemand::raw_json_string&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} +simdjson_inline simdjson_result::simdjson_result(lsx::ondemand::raw_json_string &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(error) {} - simdjson_inline simdjson_result simdjson_result::raw() const noexcept { - if (error()) { return error(); } - return first.raw(); - } - simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::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 simdjson_result::unescape_wobbly(lsx::ondemand::json_iterator& iter) const noexcept { - if (error()) { return error(); } - return first.unescape_wobbly(iter); - } +simdjson_inline simdjson_result simdjson_result::raw() const noexcept { + if (error()) { return error(); } + return first.raw(); +} +simdjson_inline char simdjson_result::operator[](size_t i) const noexcept { + if (error()) { return error(); } + return first[i]; +} +simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::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 simdjson_result::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/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): #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 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 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 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 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 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 to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); - } - - inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); - } - - inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); - } - - inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); - } - - inline simdjson_result to_json_string(simdjson_result 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 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 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&& x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); - } - inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& 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 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 */ /* 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 @@ -106160,637 +152780,91 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace ondemand { +namespace lsx { +namespace ondemand { - simdjson_inline token_iterator::token_iterator( - const uint8_t* _buf, - token_position position - ) noexcept : buf{ _buf }, _position{ position } - { - } +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 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::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 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 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 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; - } +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 ondemand +} // namespace lsx } // namespace simdjson namespace simdjson { - simdjson_inline simdjson_result::simdjson_result(lsx::ondemand::token_iterator&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} +simdjson_inline simdjson_result::simdjson_result(lsx::ondemand::token_iterator &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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-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 */ - -namespace simdjson { - namespace lsx { - namespace ondemand { - - 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 value::get_array() noexcept { - return array::start(iter); - } - simdjson_inline simdjson_result value::get_object() noexcept { - return object::start(iter); - } - simdjson_inline simdjson_result value::start_or_resume_object() noexcept { - if (iter.at_start()) { - return get_object(); - } - else { - return object::resume(iter); - } - } - - simdjson_inline simdjson_result value::get_raw_json_string() noexcept { - return iter.get_raw_json_string(); - } - simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { - return iter.get_string(allow_replacement); - } - template - 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 value::get_wobbly_string() noexcept { - return iter.get_wobbly_string(); - } - simdjson_inline simdjson_result value::get_double() noexcept { - return iter.get_double(); - } - simdjson_inline simdjson_result value::get_double_in_string() noexcept { - return iter.get_double_in_string(); - } - simdjson_inline simdjson_result value::get_uint64() noexcept { - return iter.get_uint64(); - } - simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { - return iter.get_uint64_in_string(); - } - simdjson_inline simdjson_result value::get_int64() noexcept { - return iter.get_int64(); - } - simdjson_inline simdjson_result value::get_int64_in_string() noexcept { - return iter.get_int64_in_string(); - } - simdjson_inline simdjson_result value::get_bool() noexcept { - return iter.get_bool(); - } - simdjson_inline simdjson_result value::is_null() noexcept { - return iter.is_null(); - } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } - - template simdjson_inline error_code value::get(T& out) noexcept { - return get().get(out); - } - -#if SIMDJSON_EXCEPTIONS - template - simdjson_inline value::operator T() noexcept(false) { - return get(); - } - 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 value::begin() & noexcept { - return get_array().begin(); - } - simdjson_inline simdjson_result value::end() & noexcept { - return {}; - } - simdjson_inline simdjson_result value::count_elements() & noexcept { - simdjson_result 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 value::count_fields() & noexcept { - simdjson_result answer; - auto a = get_object(); - answer = a.count_fields(); - iter.move_at_start(); - return answer; - } - simdjson_inline simdjson_result value::at(size_t index) noexcept { - auto a = get_array(); - return a.at(index); - } - - simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { - return start_or_resume_object().find_field(key); - } - simdjson_inline simdjson_result value::find_field(const char* key) noexcept { - return start_or_resume_object().find_field(key); - } - - simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { - return start_or_resume_object().find_field_unordered(key); - } - simdjson_inline simdjson_result value::find_field_unordered(const char* key) noexcept { - return start_or_resume_object().find_field_unordered(key); - } - - simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { - return start_or_resume_object()[key]; - } - simdjson_inline simdjson_result value::operator[](const char* key) noexcept { - return start_or_resume_object()[key]; - } - - simdjson_inline simdjson_result value::type() noexcept { - return iter.type(); - } - - simdjson_inline simdjson_result 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 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 value::is_integer() noexcept { - return iter.is_integer(); - } - simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { - return iter.get_number_type(); - } - simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { - return iter.get_number(); - } - - simdjson_inline std::string_view value::raw_json_token() noexcept { - return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); - } - - simdjson_inline simdjson_result 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 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::at_pointer(std::string_view json_pointer) noexcept { - 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::at_path(std::string_view json_path) noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) { - case json_type::array: - return (*this).get_array().at_path(json_path); - case json_type::object: - return (*this).get_object().at_path(json_path); - default: - return INVALID_JSON_POINTER; - } - } - - } // namespace ondemand - } // namespace lsx -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result( - lsx::ondemand::value&& value - ) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) - { - } - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept : - implementation_simdjson_result_base(error) - { - } - simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); - } - simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); - } - simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() & noexcept { - if (error()) { return error(); } - return {}; - } - - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::find_field(const char* key) noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char* key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](const char* key) noexcept { - if (error()) { return error(); } - return first[key]; - } - - simdjson_inline simdjson_result simdjson_result::get_array() noexcept { - if (error()) { return error(); } - return first.get_array(); - } - simdjson_inline simdjson_result simdjson_result::get_object() noexcept { - if (error()) { return error(); } - return first.get_object(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); - } - simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); - } - simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); - } - template - simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(receiver, allow_replacement); - } - simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); - } - simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); - } - simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); - } - simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); - } - - template simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return first.get(); - } - template simdjson_inline error_code simdjson_result::get(T& out) noexcept { - if (error()) { return error(); } - return first.get(out); - } - - template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return std::move(first); - } - template<> simdjson_inline error_code simdjson_result::get(lsx::ondemand::value& out) noexcept { - if (error()) { return error(); } - out = first; - return SUCCESS; - } - - simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); - } - simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); - } - simdjson_inline simdjson_result simdjson_result::is_string() noexcept { - if (error()) { return error(); } - return first.is_string(); - } - simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); - } - simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); - } - simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); - } - simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); - } -#if SIMDJSON_EXCEPTIONS - template - simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return static_cast(first); - } - simdjson_inline simdjson_result::operator lsx::ondemand::array() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator lsx::ondemand::object() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator lsx::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } -#endif - - simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); - } - - simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { - if (error()) { return error(); } - return first.raw_json(); - } - - simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); - } - - simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { - if (error()) { return error(); } - return first.current_depth(); - } - - simdjson_inline simdjson_result simdjson_result::at_pointer( - std::string_view json_pointer) noexcept { - if (error()) { - return error(); - } - return first.at_pointer(json_pointer); - } - - simdjson_inline simdjson_result simdjson_result::at_path( - std::string_view json_path) noexcept { - if (error()) { - return error(); - } - return first.at_path(json_path); - } - -} // namespace simdjson - -#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H -/* end file simdjson/generic/ondemand/value-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 @@ -106807,1089 +152881,2283 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lsx { - namespace ondemand { +namespace lsx { +namespace ondemand { - 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_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 value_iterator::start_object() noexcept { - SIMDJSON_TRY(start_container('{', "Not an object", "object")); - return started_object(); - } +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_object() noexcept { + SIMDJSON_TRY( start_container('{', "Not an object", "object") ); + return started_object(); +} - simdjson_warn_unused simdjson_inline simdjson_result 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 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 value_iterator::started_object() noexcept { - assert_at_container_start(); +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_object() noexcept { + assert_at_container_start(); #if SIMDJSON_DEVELOPMENT_CHECKS - _json_iter->set_start_position(_depth, start_position()); + _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(); - end_container(); - return false; - } - return true; - } + 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 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 value_iterator::started_root_object() noexcept { - auto error = check_root_object(); - if (error) { return error; } - return started_object(); - } +simdjson_warn_unused simdjson_inline simdjson_result 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 { +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"); } + 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; - } + _json_iter->ascend_to(depth()-1); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline simdjson_result value_iterator::has_next_field() noexcept { - assert_at_next(); +simdjson_warn_unused simdjson_inline simdjson_result 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"); + } +} - // 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 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; - simdjson_warn_unused simdjson_inline simdjson_result 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()) { + // + // 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; } + // 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; + 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; } + // 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; } + 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; - } + } + 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; } - } + // 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; - } + // 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 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; +SIMDJSON_PUSH_DISABLE_WARNINGS +SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING +simdjson_warn_unused simdjson_inline simdjson_result 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(); + // 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; + // 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()) { + // 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; } + // 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; } + 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; } + 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 + // 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; } + // 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; - } + // 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; } + // 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 + // 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); + // 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; - } + // 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 + // 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 value_iterator::field_key() noexcept { - assert_at_next(); +simdjson_warn_unused simdjson_inline simdjson_result 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); - } + 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_value() noexcept { - assert_at_next(); +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; - } + 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 value_iterator::start_array() noexcept { - SIMDJSON_TRY(start_container('[', "Not an array", "array")); - return started_array(); - } +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_array() noexcept { + SIMDJSON_TRY( start_container('[', "Not an array", "array") ); + return started_array(); +} - simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_root_array() noexcept { - SIMDJSON_TRY(start_container('[', "Not an array", "array")); - return started_root_array(); - } +simdjson_warn_unused simdjson_inline simdjson_result 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; - } +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 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); +simdjson_warn_unused simdjson_inline simdjson_result 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()); + _json_iter->set_start_position(_depth, start_position()); #endif - return true; - } + 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 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 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 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 value_iterator::has_next_element() noexcept { - assert_at_next(); +simdjson_warn_unused simdjson_inline simdjson_result 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"); - } - } + 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 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(!not_true); - } - simdjson_warn_unused simdjson_inline simdjson_result 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 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(!not_true); +} +simdjson_warn_unused simdjson_inline simdjson_result 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 value_iterator::get_string(bool allow_replacement) noexcept { - return get_raw_json_string().unescape(json_iter(), allow_replacement); - } - template - simdjson_warn_unused simdjson_inline error_code value_iterator::get_string(string_type& receiver, bool allow_replacement) noexcept { - std::string_view content; - auto err = get_string(allow_replacement).get(content); - if (err) { return err; } - receiver = content; - return SUCCESS; - } - simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_wobbly_string() noexcept { - return get_raw_json_string().unescape_wobbly(json_iter()); - } - simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_raw_json_string() noexcept { - auto json = peek_scalar("string"); - if (*json != '"') { return incorrect_type_error("Not a string"); } - advance_scalar("string"); - return raw_json_string(json + 1); - } - simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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 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 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 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 value_iterator::is_integer() noexcept { - return numberparsing::is_integer(peek_non_root_scalar("integer")); - } - simdjson_inline simdjson_result value_iterator::get_number_type() noexcept { - return numberparsing::get_number_type(peek_non_root_scalar("integer")); - } - simdjson_inline simdjson_result 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_warn_unused simdjson_inline simdjson_result value_iterator::get_string(bool allow_replacement) noexcept { + return get_raw_json_string().unescape(json_iter(), allow_replacement); +} +template +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; } + 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 value_iterator::get_wobbly_string() noexcept { + return get_raw_json_string().unescape_wobbly(json_iter()); +} +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_raw_json_string() noexcept { + auto json = peek_scalar("string"); + if (*json != '"') { return incorrect_type_error("Not a string"); } + advance_scalar("string"); + return raw_json_string(json+1); +} +simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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 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 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 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 value_iterator::is_integer() noexcept { + return numberparsing::is_integer(peek_non_root_scalar("integer")); +} +simdjson_inline simdjson_result value_iterator::get_number_type() noexcept { + return numberparsing::get_number_type(peek_non_root_scalar("integer")); +} +simdjson_inline simdjson_result 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 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 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 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.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 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.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 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 - 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; - auto err = get_root_string(check_trailing, allow_replacement).get(content); - if (err) { return err; } - receiver = content; - return SUCCESS; - } - simdjson_warn_unused simdjson_inline simdjson_result 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 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; } - advance_scalar("string"); - return raw_json_string(json + 1); - } - simdjson_warn_unused simdjson_inline simdjson_result 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 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 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; - } +simdjson_inline simdjson_result 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.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 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.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 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 +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; } + 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 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 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; } + advance_scalar("string"); + return raw_json_string(json+1); +} +simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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(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 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 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.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; - } + 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 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.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 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.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 value_iterator::get_root_bool(bool check_trailing) noexcept { - auto max_len = peek_root_length(); - auto json = peek_root_scalar("bool"); - uint8_t tmpbuf[5 + 1 + 1]; // +1 for null termination - tmpbuf[5 + 1] = '\0'; // make sure that buffer is always null terminated. - if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 5 + 1)) { return incorrect_type_error("Not a boolean"); } - auto result = parse_bool(tmpbuf); - if (result.error() == SUCCESS) { - if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; } - advance_root_scalar("bool"); - } - return result; - } - simdjson_inline simdjson_result 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"); - } - return result; - } +simdjson_warn_unused simdjson_inline simdjson_result 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.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 error_code value_iterator::skip_child() noexcept { - SIMDJSON_ASSUME(_json_iter->token._position > _start_position); - SIMDJSON_ASSUME(_json_iter->_depth >= _depth); +simdjson_warn_unused simdjson_inline simdjson_result 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; +} - return _json_iter->skip_child(depth()); - } +simdjson_inline simdjson_result 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_inline value_iterator value_iterator::child() const noexcept { - assert_at_child(); - return { _json_iter, depth() + 1, _json_iter->token.position() }; - } +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 ); - // 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 + return _json_iter->skip_child(depth()); +} - simdjson_inline bool value_iterator::at_end() const noexcept { - return _json_iter->at_end(); - } +simdjson_inline value_iterator value_iterator::child() const noexcept { + assert_at_child(); + return { _json_iter, depth()+1, _json_iter->token.position() }; +} - simdjson_inline bool value_iterator::at_start() const noexcept { - return _json_iter->token.position() == start_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_first_field() const noexcept { - SIMDJSON_ASSUME(_json_iter->token._position > _start_position); - return _json_iter->token.position() == start_position() + 1; - } +simdjson_inline bool value_iterator::at_end() const noexcept { + return _json_iter->at_end(); +} - simdjson_inline void value_iterator::abandon() noexcept { - _json_iter->abandon(); - } +simdjson_inline bool value_iterator::at_start() const noexcept { + return _json_iter->token.position() == start_position(); +} - 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 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 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 void value_iterator::abandon() noexcept { + _json_iter->abandon(); +} - 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(); } +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; +} - // 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 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 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; } +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(); - _json_iter->return_current_and_advance(); - _json_iter->ascend_to(depth() - 1); - } + // 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 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()) { +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; } + 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(); - } + 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; - } + 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(); } +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_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(); - } + 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; } +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_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); - } + 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 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_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. - return _depth == _json_iter->_depth && *_json_iter->peek() == '"'; - } +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; - } +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_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_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); - } +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_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::move_at_container_start() noexcept { + _json_iter->_depth = _depth; + _json_iter->token.set_position(_start_position + 1); +} - simdjson_inline simdjson_result value_iterator::reset_array() noexcept { - if (error()) { return error(); } - move_at_container_start(); - return started_array(); - } +simdjson_inline simdjson_result value_iterator::reset_array() noexcept { + if(error()) { return error(); } + move_at_container_start(); + return started_array(); +} - simdjson_inline simdjson_result value_iterator::reset_object() noexcept { - if (error()) { return error(); } - move_at_container_start(); - return started_object(); - } +simdjson_inline simdjson_result 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_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_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_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); - } +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 bool value_iterator::is_valid() const noexcept { + return _json_iter != nullptr; +} - simdjson_inline simdjson_result 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 TAPE_ERROR; - } - } +simdjson_inline simdjson_result 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::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::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::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 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); - } +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 ondemand +} // namespace lsx } // namespace simdjson namespace simdjson { - simdjson_inline simdjson_result::simdjson_result(lsx::ondemand::value_iterator&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} +simdjson_inline simdjson_result::simdjson_result(lsx::ondemand::value_iterator &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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 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 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 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 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 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 to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result 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 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 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&& x) { + if (x.error()) { throw simdjson::simdjson_error(x.error()); } + return (out << x.value()); +} +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& 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 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 emails; }; + * + * std::string email; + * path_accessor::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 +#include +#include + +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 +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 +consteval auto make_field_step(const char (&name)[N]) { + return path_step(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 +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 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 +struct path_accessor { + using value = ::simdjson::lsx::ondemand::value; + + static constexpr auto parser = json_path_parser(); + 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 + static inline simdjson_result access(DocOrValue& doc_or_val) noexcept { + // Validate path at compile time if T is a struct + if constexpr (std::is_class_v) { + 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(doc_or_val.get_value()); + } + + // Extract value at path directly into target with compile-time type validation + // Example: std::string name; path_accessor::extract_field(doc, name); + template + static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept { + static_assert(std::is_class_v, "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(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 + static consteval std::enable_if_t, 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 + static inline error_code extract_with_reflection(simdjson_result 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(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(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_value, target_ref); + } + } + } + // Skip unexpected characters and continue + else { + return extract_with_reflection(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 + static inline simdjson_result access_impl(simdjson_result 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_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_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_value); + } + } else { + return access_impl(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(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 + 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 + 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 + static consteval bool is_container_type() { + using BaseType = std::remove_cvref_t; + if constexpr (requires { typename BaseType::value_type; }) { + return true; + } + if constexpr (std::is_array_v) { + return true; + } + return false; + } + + // Extract element type from non-reflected container + template + using extract_element_type = std::conditional_t< + requires { typename std::remove_cvref_t::value_type; }, + typename std::remove_cvref_t::value_type, + std::conditional_t< + std::is_array_v>, + std::remove_extent_t>, + void + > + >; + + // Validate path matches struct definition + static consteval bool validate_path() { + if constexpr (!std::is_class_v) { + 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 +inline simdjson_result<::simdjson::lsx::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = path_accessor; + return accessor::access(doc_or_val); +} + +// Overload without type parameter (no validation) +template +inline simdjson_result<::simdjson::lsx::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = path_accessor; + return accessor::access(doc_or_val); +} + +// ============================================================================ +// JSON Pointer Compile-Time Support (RFC 6901) +// ============================================================================ + +// JSON Pointer parser: /field/0/nested (slash-separated) +template +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 +struct pointer_accessor { + using parser = json_pointer_parser; + 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) { + 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::is_array_like_reflected(current_type)) { + return false; + } + current_type = path_accessor::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 + static inline simdjson_result access_impl(simdjson_result 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(next_value); + } else { + auto obj = current.get_object().value_unsafe(); + auto next_value = obj.find_field_unordered(token); + return access_impl(next_value); + } + } + } + + // Access JSON value at pointer + template + static inline simdjson_result access(DocOrValue& doc_or_val) noexcept { + if constexpr (std::is_class_v) { + 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 current = doc_or_val.get_value(); + return access_impl<0>(current); + } + + // Extract value at pointer directly into target with type validation + template + static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept { + static_assert(std::is_class_v, "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 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 + static consteval std::enable_if_t, 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::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 +inline simdjson_result<::simdjson::lsx::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = pointer_accessor; + return accessor::access(doc_or_val); +} + +// Overload without type parameter (no validation) +template +inline simdjson_result<::simdjson::lsx::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = pointer_accessor; + 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 */ @@ -107914,6 +155182,13 @@ namespace simdjson { /* begin file simdjson/lasx/begin.h */ /* defining SIMDJSON_IMPLEMENTATION to "lasx" */ #define SIMDJSON_IMPLEMENTATION lasx +#include // 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 @@ -107924,21 +155199,21 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - /** - * Implementation for LASX. - */ - namespace lasx { +/** + * Implementation for LASX. + */ +namespace lasx { - class implementation; +class implementation; - namespace { - namespace simd { - template struct simd8; - template struct simd8x64; - } // namespace simd - } // unnamed namespace +namespace { +namespace simd { +template struct simd8; +template struct simd8x64; +} // namespace simd +} // unnamed namespace - } // namespace lasx +} // namespace lasx } // namespace simdjson #endif // SIMDJSON_LASX_BASE_H @@ -107952,8 +155227,7 @@ namespace simdjson { /* amalgamation skipped (editor-only): #include "simdjson/lasx/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 #include static_assert(sizeof(__m256i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for LoongArch ASX"); @@ -107972,43 +155246,43 @@ static_assert(sizeof(__m256i) <= simdjson::SIMDJSON_PADDING, "insufficient paddi /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace { +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); - } +// 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 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 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); - } +/* 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); +} - simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t* result) { - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); - } +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t *result) { + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); +} - } // unnamed namespace - } // namespace lasx +} // unnamed namespace +} // namespace lasx } // namespace simdjson #endif // SIMDJSON_LASX_BITMANIPULATION_H @@ -108023,26 +155297,26 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace { +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; - } +// +// 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 +} // unnamed namespace +} // namespace lasx } // namespace simdjson #endif @@ -108061,33 +155335,39 @@ namespace simdjson { #include namespace simdjson { - namespace lasx { - namespace numberparsing { +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); - } +// 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; - } +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 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 */ @@ -108103,369 +155383,370 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace { - namespace simd { +namespace lasx { +namespace { +namespace simd { - // Forward-declared so they can be used by splat and friends. - template - struct base { - __m256i value; + // Forward-declared so they can be used by splat and friends. + template + struct base { + __m256i value; - // Zero constructor - simdjson_inline base() : value{ __m256i() } {} + // Zero constructor + simdjson_inline base() : value{__m256i()} {} - // Conversion from SIMD register - simdjson_inline base(const __m256i _value) : value(_value) {} + // 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; } + // 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(this); *this_cast = *this_cast | other; return *this_cast; } - simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } - simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } - }; + // 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(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; - // Forward-declared so they can be used by splat and friends. - template - struct simd8; + // Forward-declared so they can be used by splat and friends. + template + struct simd8; - template> - struct base8 : base> { - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m256i _value) : base>(_value) {} + template> + struct base8: base> { + simdjson_inline base8() : base>() {} + simdjson_inline base8(const __m256i _value) : base>(_value) {} - friend simdjson_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return __lasx_xvseq_b(lhs, rhs); } + friend simdjson_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return __lasx_xvseq_b(lhs, rhs); } - static const int SIZE = sizeof(base>::value); + static const int SIZE = sizeof(base>::value); - template - simdjson_inline simd8 prev(const simd8 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); - } - }; + template + simdjson_inline simd8 prev(const simd8 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 : base8 { - static simdjson_inline simd8 splat(bool _value) { return __lasx_xvreplgr2vr_b(uint8_t(-(!!_value))); } + // SIMD byte mask type (returned by things like eq and gt) + template<> + struct simd8: base8 { + static simdjson_inline simd8 splat(bool _value) { return __lasx_xvreplgr2vr_b(uint8_t(-(!!_value))); } - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m256i _value) : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) : base8(splat(_value)) {} + simdjson_inline simd8() : base8() {} + simdjson_inline simd8(const __m256i _value) : base8(_value) {} + // Splat constructor + simdjson_inline simd8(bool _value) : base8(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 operator~() const { return *this ^ true; } - }; + 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 operator~() const { return *this ^ true; } + }; - template - struct base8_numeric : base8 { - static simdjson_inline simd8 splat(T _value) { - return __lasx_xvreplgr2vr_b(_value); - } - static simdjson_inline simd8 zero() { return __lasx_xvldi(0); } - static simdjson_inline simd8 load(const T values[32]) { - return __lasx_xvld(reinterpret_cast(values), 0); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdjson_inline simd8 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( - 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 - ); - } + template + struct base8_numeric: base8 { + static simdjson_inline simd8 splat(T _value) { + return __lasx_xvreplgr2vr_b(_value); + } + static simdjson_inline simd8 zero() { return __lasx_xvldi(0); } + static simdjson_inline simd8 load(const T values[32]) { + return __lasx_xvld(reinterpret_cast(values), 0); + } + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdjson_inline simd8 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( + 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() {} - simdjson_inline base8_numeric(const __m256i _value) : base8(_value) {} + simdjson_inline base8_numeric() : base8() {} + simdjson_inline base8_numeric(const __m256i _value) : base8(_value) {} - // Store to array - simdjson_inline void store(T dst[32]) const { - return __lasx_xvst(*this, reinterpret_cast<__m256i*>(dst), 0); - } + // 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 operator+(const simd8 other) const { return __lasx_xvadd_b(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return __lasx_xvsub_b(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { return __lasx_xvadd_b(*this, other); } + simdjson_inline simd8 operator-(const simd8 other) const { return __lasx_xvsub_b(*this, other); } + simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } + simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } - // Override to distinguish from bool version - simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } + // Override to distinguish from bool version + simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return __lasx_xvshuf_b(lookup_table, lookup_table, *this); - } + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) + template + simdjson_inline simd8 lookup_16(simd8 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 - 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]; + // 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 + 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(reinterpret_cast(pshufb_combine_table)), pop1 * 8); - __m256i maskhi = __lasx_xvldx(reinterpret_cast(reinterpret_cast(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(output), 0); - uint64_t value3 = __lasx_xvpickve2gr_du(answer, 2); - uint64_t value4 = __lasx_xvpickve2gr_du(answer, 3); - uint64_t* pos = reinterpret_cast(reinterpret_cast(output) + 16 - (pop1 + pop2) / 2); - pos[0] = value3; - pos[1] = value4; - } + // then load the corresponding mask + __m256i masklo = __lasx_xvldx(reinterpret_cast(reinterpret_cast(pshufb_combine_table)), pop1 * 8); + __m256i maskhi = __lasx_xvldx(reinterpret_cast(reinterpret_cast(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(output), 0); + uint64_t value3 = __lasx_xvpickve2gr_du(answer, 2); + uint64_t value4 = __lasx_xvpickve2gr_du(answer, 3); + uint64_t *pos = reinterpret_cast(reinterpret_cast(output) + 16 - (pop1 + pop2) / 2); + pos[0] = value3; + pos[1] = value4; + } - template - simdjson_inline simd8 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::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - }; + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; - // Signed bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( - 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 - ); - } + // Signed bytes + template<> + struct simd8 : base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( + 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 max_val(const simd8 other) const { return __lasx_xvmax_b(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return __lasx_xvmin_b(*this, other); } - simdjson_inline simd8 operator>(const simd8 other) const { return __lasx_xvslt_b(other, *this); } - simdjson_inline simd8 operator<(const simd8 other) const { return __lasx_xvslt_b(*this, other); } - }; + // Order-sensitive comparisons + simdjson_inline simd8 max_val(const simd8 other) const { return __lasx_xvmax_b(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return __lasx_xvmin_b(*this, other); } + simdjson_inline simd8 operator>(const simd8 other) const { return __lasx_xvslt_b(other, *this); } + simdjson_inline simd8 operator<(const simd8 other) const { return __lasx_xvslt_b(*this, other); } + }; - // Unsigned bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( - 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 - ); - } + // Unsigned bytes + template<> + struct simd8: base8_numeric { + simdjson_inline simd8() : base8_numeric() {} + simdjson_inline simd8(const __m256i _value) : base8_numeric(_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 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( + 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 saturating_add(const simd8 other) const { return __lasx_xvsadd_bu(*this, other); } - simdjson_inline simd8 saturating_sub(const simd8 other) const { return __lasx_xvssub_bu(*this, other); } + // Saturated math + simdjson_inline simd8 saturating_add(const simd8 other) const { return __lasx_xvsadd_bu(*this, other); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return __lasx_xvssub_bu(*this, other); } - // Order-specific operations - simdjson_inline simd8 max_val(const simd8 other) const { return __lasx_xvmax_bu(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return __lasx_xvmin_bu(other, *this); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } - simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } - simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } - simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } + // Order-specific operations + simdjson_inline simd8 max_val(const simd8 other) const { return __lasx_xvmax_bu(*this, other); } + simdjson_inline simd8 min_val(const simd8 other) const { return __lasx_xvmin_bu(other, *this); } + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } + // Same as <, but only guarantees true is nonzero (< guarantees true = -1) + simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } + simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } + simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } + simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } + simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } - // Bit-specific operations - simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } - simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } - simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } - simdjson_inline simd8 any_bits_set(simd8 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 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 bits) const { return !bits_not_set_anywhere(bits); } - template - simdjson_inline simd8 shr() const { return simd8(__lasx_xvsrli_b(*this, N)); } - template - simdjson_inline simd8 shl() const { return simd8(__lasx_xvslli_b(*this, N)); } - }; + // Bit-specific operations + simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } + simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } + simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } + simdjson_inline simd8 any_bits_set(simd8 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 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 bits) const { return !bits_not_set_anywhere(bits); } + template + simdjson_inline simd8 shr() const { return simd8(__lasx_xvsrli_b(*this, N)); } + template + simdjson_inline simd8 shl() const { return simd8(__lasx_xvslli_b(*this, N)); } + }; - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 2, "LASX kernel should use two registers per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; + template + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + static_assert(NUM_CHUNKS == 2, "LASX kernel should use two registers per 64-byte block."); + const simd8 chunks[NUM_CHUNKS]; + template simd8 get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8(); } - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed + simd8x64(const simd8x64& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& other) = delete; // no assignment allowed + simd8x64() = delete; // no default constructor allowed - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1) : chunks{ chunk0, chunk1 } {} - simdjson_inline simd8x64(const T ptr[64]) : chunks{ simd8::load(ptr), simd8::load(ptr + 32) } {} + simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1) : chunks{chunk0, chunk1} {} + simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::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 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) * 0); - this->chunks[1].store(ptr + sizeof(simd8) * 1); - } + simdjson_inline void store(T ptr[64]) const { + this->chunks[0].store(ptr+sizeof(simd8)*0); + this->chunks[1].store(ptr+sizeof(simd8)*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 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 reduce_or() const { - return this->chunks[0] | this->chunks[1]; - } + simdjson_inline simd8 reduce_or() const { + return this->chunks[0] | this->chunks[1]; + } - simdjson_inline uint64_t eq(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] == mask, - this->chunks[1] == mask - ).to_bitmask(); - } + simdjson_inline uint64_t eq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] == mask, + this->chunks[1] == mask + ).to_bitmask(); + } - simdjson_inline uint64_t eq(const simd8x64& other) const { - return simd8x64( - this->chunks[0] == other.chunks[0], - this->chunks[1] == other.chunks[1] - ).to_bitmask(); - } + simdjson_inline uint64_t eq(const simd8x64 &other) const { + return simd8x64( + 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 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] <= mask, - this->chunks[1] <= mask - ).to_bitmask(); - } - }; // struct simd8x64 + simdjson_inline uint64_t lteq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64( + this->chunks[0] <= mask, + this->chunks[1] <= mask + ).to_bitmask(); + } + }; // struct simd8x64 - } // namespace simd - } // unnamed namespace - } // namespace lasx +} // namespace simd +} // unnamed namespace +} // namespace lasx } // namespace simdjson #endif // SIMDJSON_LASX_SIMD_H @@ -108482,50 +155763,77 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace { +namespace lasx { +namespace { - using namespace simd; +using namespace simd; - // Holds backslashes and quotes locations. - struct backslash_and_quote { - public: - static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t* src, uint8_t* dst); +// 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); } + 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 + 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 v(src); - v.store(dst); - return { - static_cast((v == '\\').to_bitmask()), // bs_bits - static_cast((v == '"').to_bitmask()), // quote_bits - }; - } +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 v(src); + v.store(dst); + return { + static_cast((v == '\\').to_bitmask()), // bs_bits + static_cast((v == '"').to_bitmask()), // quote_bits + }; +} - } // unnamed namespace - } // namespace lasx + +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 v(src); + v.store(dst); + simd8 is_quote = (v == '"'); + simd8 is_backslash = (v == '\\'); + simd8 is_control = (v < 32); + return { + static_cast((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) +#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_BUILDER_DEPENDENCIES_H) #error simdjson/generic/ondemand/dependencies.h must be included before simdjson/generic/ondemand/amalgamated.h! #endif @@ -108540,46 +155848,141 @@ namespace simdjson { /* 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 { +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; +/** 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; +/** @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*; +/** @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; +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; - } // namespace ondemand - } // namespace lasx +} // 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 struct is_builtin_deserializable : std::false_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; + +template +concept is_builtin_deserializable_v = is_builtin_deserializable::value; + +template +concept custom_deserializable = tag_invocable; + +template +concept deserializable = custom_deserializable || is_builtin_deserializable_v || concepts::optional_type; + +template +concept nothrow_custom_deserializable = nothrow_tag_invocable; + +// built-in types are noexcept and if an error happens, the value simply gets ignored and the error is returned. +template +concept nothrow_deserializable = nothrow_custom_deserializable || is_builtin_deserializable_v; + +/// 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 + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(array_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for object + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(object_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for value + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document reference + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + 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 @@ -108591,485 +155994,490 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace ondemand { +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{}; +/** + * 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; +public: + simdjson_inline value_iterator() noexcept = default; - /** - * Denote that we're starting a document. - */ - simdjson_inline void start_document() noexcept; + /** + * 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; + /** + * 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 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 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 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; + /** + * 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; + /** + * 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 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 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 type() 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 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. - * - * @{ - */ + /** + * @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 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 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). - */ - simdjson_warn_unused simdjson_inline simdjson_result 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). - */ - simdjson_warn_unused simdjson_inline simdjson_result started_root_object() noexcept; + /** + * 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 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 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 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 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 has_next_field() 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 has_next_field() noexcept; - /** - * Get the current field's key. - */ - simdjson_warn_unused simdjson_inline simdjson_result field_key() noexcept; + /** + * Get the current field's key. + */ + simdjson_warn_unused simdjson_inline simdjson_result field_key() noexcept; - /** - * Pass the : in the field and move to its value. - */ - simdjson_warn_unused simdjson_inline error_code field_value() 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. + * + * 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 find_field_raw(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 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 find_field_unordered_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 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. - * @{ - */ + /** + * @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 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 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). - */ - simdjson_warn_unused simdjson_inline simdjson_result 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). - */ - simdjson_warn_unused simdjson_inline simdjson_result started_root_array() noexcept; + /** + * 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 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 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 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 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 has_next_element() 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 has_next_element() noexcept; - /** - * Get a child value iterator. - */ - simdjson_warn_unused simdjson_inline value_iterator child() const noexcept; + /** + * Get a child value iterator. + */ + simdjson_warn_unused simdjson_inline value_iterator child() const noexcept; - /** @} */ + /** @} */ - /** - * @defgroup scalar Scalar values - * @addtogroup scalar - * @{ - */ + /** + * @defgroup scalar Scalar values + * @addtogroup scalar + * @{ + */ - simdjson_warn_unused simdjson_inline simdjson_result get_string(bool allow_replacement) noexcept; - template - simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_int64() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_double() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_bool() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_null() noexcept; - simdjson_warn_unused simdjson_inline bool is_negative() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_integer() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_number() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_string(bool allow_replacement) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_int64() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_double() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_bool() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result is_null() noexcept; + simdjson_warn_unused simdjson_inline bool is_negative() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result is_integer() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_number() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_string(bool check_trailing, bool allow_replacement) noexcept; - template - 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 get_root_wobbly_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_raw_json_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64_in_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_int64(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_int64_in_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_double(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_double_in_string(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_bool(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline bool is_root_negative() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_root_integer(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_number_type(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_number(bool check_trailing) noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_root_null(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_string(bool check_trailing, bool allow_replacement) noexcept; + template + 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 get_root_wobbly_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_raw_json_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64_in_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_int64(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_int64_in_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_double(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_double_in_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_bool(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline bool is_root_negative() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result is_root_integer(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_number_type(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_number(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result is_root_null(bool check_trailing) noexcept; - 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_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; + 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 reset_array() noexcept; - /** - * Restarts an object iteration. - * @returns Whether the object has any fields (returns false for empty). - */ - simdjson_inline simdjson_result 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; + /** @} */ +protected: + /** + * Restarts an array iteration. + * @returns Whether the array has any elements (returns false for empty). + */ + simdjson_inline simdjson_result reset_array() noexcept; + /** + * Restarts an object iteration. + * @returns Whether the object has any fields (returns false for empty). + */ + simdjson_inline simdjson_result 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; + /** + * 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 parse_null(const uint8_t* json) const noexcept; - simdjson_inline simdjson_result 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; + simdjson_inline simdjson_result parse_null(const uint8_t *json) const noexcept; + simdjson_inline simdjson_result 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. - */ + /** + * 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 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_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_inline error_code start_container(uint8_t start_char, const char* incorrect_type_message, const char* type) noexcept; - simdjson_inline error_code end_container() 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 advance_to_value() 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 advance_to_value() noexcept; - simdjson_inline error_code incorrect_type_error(const char* message) const noexcept; - simdjson_inline error_code error_unless_more_tokens(uint32_t tokens = 1) const 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; + 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; + /** + * 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; + 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; + /** 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; - /** @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_inline error_code report_error(error_code error, const char* message) noexcept; + /** @copydoc error_code json_iterator::position() const noexcept; */ + simdjson_inline token_position position() const 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 array; - friend class value; - friend class field; - }; // value_iterator + 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 ondemand +} // namespace lasx } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public lasx::implementation_simdjson_result_base { - 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; - }; +template<> +struct simdjson_result : public lasx::implementation_simdjson_result_base { +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 @@ -109084,776 +156492,876 @@ namespace simdjson { /* 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 */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ +#include + 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; +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, uint64_t, int64_t, double, bool - * - * You may use get_double(), get_bool(), get_uint64(), get_int64(), - * 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 simdjson_inline simdjson_result get() noexcept { - // 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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " 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."); - } + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool + * + * You may use get_double(), get_bool(), get_uint64(), get_int64(), + * 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 + simdjson_inline simdjson_result get() +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } - /** - * Get this value as the given type. - * - * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool - * - * @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 simdjson_inline error_code get(T& out) noexcept; - /** - * 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 get_array() noexcept; + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + #if SIMDJSON_SUPPORTS_CONCEPTS + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else if constexpr (concepts::optional_type) { + using value_type = typename std::remove_cvref_t::value_type; - /** - * 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 get_object() noexcept; + // 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; + } - /** - * 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 get_uint64() noexcept; + if (!out) { + out.emplace(); + } + return get(out.value()); + } else { + static_assert(!sizeof(T), "The get 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 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, double, or bool."); + static_cast(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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " 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(out); // to get rid of unused errors + return UNINITIALIZED; +#endif + } - /** - * 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 get_uint64_in_string() noexcept; + /** + * 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 get_array() 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 get_int64() 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 get_object() 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 get_int64_in_string() 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 get_uint64() 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 get_double() 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 get_uint64_in_string() 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 get_double_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 get_int64() noexcept; - /** - * Cast this JSON value to a string. - * - * The string is guaranteed to be valid UTF-8. - * - * Equivalent to get(). - * - * Important: a value should be consumed once. Calling get_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 get_string(bool allow_replacement = false) 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 get_int64_in_string() noexcept; - /** - * 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 - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) 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 get_double() 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 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 get_raw_json_string() 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 get_double_in_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 get_bool() noexcept; + /** + * Cast this JSON value to a string. + * + * The string is guaranteed to be valid UTF-8. + * + * Equivalent to get(). + * + * 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 get_string(bool allow_replacement = false) 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 is_null() noexcept; + /** + * 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 + 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 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 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 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 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 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 - 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 signed 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(). - * - * @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); + /** + * Cast this JSON value to an instance of type T. The programmer is responsible for + * providing an implementation of get 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 + 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 signed 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(). + * + * @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 begin() & noexcept; - /** - * Sentinel representing the end of the array. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result 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 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 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 at(size_t index) noexcept; - /** - * Look up a field by name on an object (order-sensitive). - * - * 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 }`: - * - * ```c++ - * 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. + /** + * Begin array iteration. + * + * Part of the std::iterable interface. + * + * @returns INCORRECT_TYPE If the JSON value is not an array. + */ + simdjson_inline simdjson_result begin() & noexcept; + /** + * Sentinel representing the end of the array. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result 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 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 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 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 find_field(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ - simdjson_inline simdjson_result find_field(const char* key) noexcept; + * **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 find_field(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ + simdjson_inline simdjson_result 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. - * - * It is the default, however, 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 find_field_unordered(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result find_field_unordered(const char* key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](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 find_field_unordered(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result find_field_unordered(const char *key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result 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 type() noexcept; + /** + * 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 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 is_scalar() noexcept; - /** - * Checks whether the value is a string. - * - * @returns true if the type is string - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". - */ - simdjson_inline simdjson_result is_string() noexcept; + /** + * Checks whether the value is a 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 is_scalar() noexcept; + /** + * Checks whether the value is a string. + * + * @returns true if the type is string + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + */ + simdjson_inline simdjson_result is_string() noexcept; - /** - * Checks whether the value is a negative number. - * - * @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 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 get_number_type() 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 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 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 get_number() 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 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 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 raw_json() 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 raw_json() noexcept; - /** - * Returns the current location in the document if in bounds. - */ - simdjson_inline simdjson_result current_location() noexcept; + /** + * Returns the current location in the document if in bounds. + */ + simdjson_inline simdjson_result 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; + /** + * 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 at_pointer(std::string_view json_pointer) 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 at_pointer(std::string_view json_pointer) noexcept; - /** - * Get the value associated with the given JSONPath expression. We only support - * JSONPath queries that trivially convertible to JSON Pointer queries: key - * names and array indices. - * - * @return The value associated with the given JSONPath expression, or: - * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - */ - simdjson_inline simdjson_result at_path(std::string_view at_path) noexcept; + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + simdjson_inline simdjson_result at_path(std::string_view at_path) noexcept; + /** + * Get all values matching the given JSONPath expression with wildcard support. + * Supports wildcard character (*) for arrays or ".*" for objects. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern + */ + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; - protected: - /** - * Create a value. - */ - simdjson_inline value(const value_iterator& iter) 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; + /** + * 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; + /** + * 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; + /** + * 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 start_or_resume_object() noexcept; + /** + * Get the object, starting or resuming it as necessary + */ + simdjson_inline simdjson_result 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; + // simdjson_inline void log_value(const char *type) const noexcept; + // simdjson_inline void log_error(const char *message) const noexcept; - value_iterator iter{}; + value_iterator iter{}; - friend class document; - friend class array_iterator; - friend class field; - friend class object; - friend struct simdjson_result; - friend struct simdjson_result; - friend class field; - }; + friend class document; + friend class array_iterator; + friend class field; + friend class object; + friend struct simdjson_result; + friend struct simdjson_result; + friend class field; +}; - } // namespace ondemand - } // namespace lasx +} // namespace ondemand +} // namespace lasx } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public lasx::implementation_simdjson_result_base { - 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; +template<> +struct simdjson_result : public lasx::implementation_simdjson_result_base { +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 get_array() noexcept; - simdjson_inline simdjson_result get_object() noexcept; + simdjson_inline simdjson_result get_array() noexcept; + simdjson_inline simdjson_result get_object() noexcept; - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result is_null() noexcept; + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result is_null() noexcept; - template simdjson_inline simdjson_result get() noexcept; + template simdjson_inline simdjson_result get() noexcept; - template simdjson_inline error_code get(T& out) noexcept; + template simdjson_inline error_code get(T &out) noexcept; #if SIMDJSON_EXCEPTIONS - template - 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); + template + 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 count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; + simdjson_inline simdjson_result count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result end() & noexcept; - /** - * Look up a field by name on an object (order-sensitive). - * - * 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 }`: - * - * ```c++ - * 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 find_field(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ - simdjson_inline simdjson_result find_field(const char* key) 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 find_field(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ + simdjson_inline simdjson_result 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. - * - * It is the default, however, 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 find_field_unordered(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result find_field_unordered(const char* key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](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 find_field_unordered(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result find_field_unordered(const char *key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result operator[](int) noexcept = delete; - /** - * Get the type of this JSON value. - * - * 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 type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_string() noexcept; - simdjson_inline simdjson_result is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; + /** + * 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 type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + simdjson_inline simdjson_result is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; - /** @copydoc simdjson_inline std::string_view value::raw_json_token() const noexcept */ - simdjson_inline simdjson_result raw_json_token() noexcept; - simdjson_inline simdjson_result raw_json() noexcept; + /** @copydoc simdjson_inline std::string_view value::raw_json_token() const noexcept */ + simdjson_inline simdjson_result raw_json_token() noexcept; + simdjson_inline simdjson_result raw_json() noexcept; - /** @copydoc simdjson_inline simdjson_result current_location() noexcept */ - simdjson_inline simdjson_result current_location() noexcept; - /** @copydoc simdjson_inline int32_t current_depth() const noexcept */ - simdjson_inline simdjson_result current_depth() const noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - }; + /** @copydoc simdjson_inline simdjson_result current_location() noexcept */ + simdjson_inline simdjson_result current_location() noexcept; + /** @copydoc simdjson_inline int32_t current_depth() const noexcept */ + simdjson_inline simdjson_result current_depth() const noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; +}; } // namespace simdjson @@ -109869,53 +157377,53 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace ondemand { +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 { +// 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 - }; +enum class log_level : int32_t { + info = 0, + error = 1 +}; #if SIMDJSON_VERBOSE_LOGGING - static constexpr const bool LOG_ENABLED = true; + static constexpr const bool LOG_ENABLED = true; #else - static constexpr const bool LOG_ENABLED = false; + 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 - 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 - 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; +// 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 +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 +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_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; +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 logger +} // namespace ondemand +} // namespace lasx } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_LOGGER_H @@ -109932,150 +157440,150 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace ondemand { +namespace lasx { +namespace ondemand { - /** - * Iterates through JSON tokens (`{` `}` `[` `]` `,` `:` `""` `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; +/** + * Iterates through JSON tokens (`{` `}` `[` `]` `,` `:` `""` `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; + /** + * 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; + /** + * 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. + // 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; + 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; +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; + /** + * 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{}; + const uint8_t *buf{}; + token_position _position{}; - friend class json_iterator; - friend class value_iterator; - friend class object; - template - friend simdjson_inline void logger::log_line(const json_iterator& iter, const char* title_prefix, const char* title, std::string_view detail, int delta, int depth_delta, logger::log_level level, Args&&... args) noexcept; - template - 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; - }; + friend class json_iterator; + friend class value_iterator; + friend class object; + template + friend simdjson_inline void logger::log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta, logger::log_level level, Args&&... args) noexcept; + template + 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 ondemand +} // namespace lasx } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public lasx::implementation_simdjson_result_base { - 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 - }; +template<> +struct simdjson_result : public lasx::implementation_simdjson_result_base { +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 @@ -110093,330 +157601,330 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace ondemand { +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 }; +/** + * 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}; - 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; +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 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; + /** + * 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; - /** - * 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; + /** + * 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 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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 depth. + */ + simdjson_inline depth_t depth() const noexcept; - /** - * Get current (writeable) location in the string buffer. - */ - simdjson_inline uint8_t*& string_buf_loc() 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_inline error_code report_error(error_code error, const char* message) 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_inline error_code optional_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; + /** + * 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 unescape(raw_json_string in, bool allow_replacement) noexcept; - simdjson_inline simdjson_result unescape_wobbly(raw_json_string in) 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 unescape(raw_json_string in, bool allow_replacement) noexcept; + simdjson_inline simdjson_result unescape_wobbly(raw_json_string in) noexcept; - simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept; + simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept; - simdjson_inline error_code consume_character(char c) 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; + 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; + /* 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 current_location() const noexcept; + /** + * Returns the current location in the document if in bounds. + */ + inline simdjson_result 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; + /** + * 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; + 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; + /// 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 - 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 - 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 + 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 + 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 + 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 ondemand +} // namespace lasx } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public lasx::implementation_simdjson_result_base { - public: - simdjson_inline simdjson_result(lasx::ondemand::json_iterator&& value) noexcept; ///< @private - simdjson_inline simdjson_result(error_code error) noexcept; ///< @private +template<> +struct simdjson_result : public lasx::implementation_simdjson_result_base { +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; - }; + simdjson_inline simdjson_result() noexcept = default; +}; } // namespace simdjson @@ -110434,152 +157942,153 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace ondemand { +namespace lasx { +namespace ondemand { - /** - * The type of a JSON value. - */ - enum class json_type { - // 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) - }; +/** + * 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 { +/** + * 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 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::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; + /** + * 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; + /** + * 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 - 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 - 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. - */ +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 + 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 + 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 }; - }; + /** + * 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; +/** + * 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& type) noexcept(false); +/** + * 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 &type) noexcept(false); #endif - } // namespace ondemand - } // namespace lasx +} // namespace ondemand +} // namespace lasx } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public lasx::implementation_simdjson_result_base { - 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 - }; +template<> +struct simdjson_result : public lasx::implementation_simdjson_result_base { +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 @@ -110596,200 +158105,206 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace ondemand { +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; +/** + * 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; + /** + * 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; - /** - * 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; + /** + * 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). - * 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 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 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). + * 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 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). + * 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 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; + /** + * 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; - /** - * 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; + /** + * 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; - private: + /** + * 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; } + /** + * 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; } + /** + * 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 unescape(json_iterator& iter, bool allow_replacement) const noexcept; + /** + * 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 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 unescape_wobbly(json_iterator& iter) const noexcept; - const uint8_t* buf{}; - friend class object; - friend class field; - friend class parser; - friend struct simdjson_result; - }; + /** + * 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 unescape_wobbly(json_iterator &iter) const noexcept; + const uint8_t * buf{}; + friend class object; + friend class field; + friend class parser; + friend struct simdjson_result; +}; - simdjson_unused simdjson_inline std::ostream& operator<<(std::ostream&, const raw_json_string&) noexcept; +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; +/** + * 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 ondemand +} // namespace lasx } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public lasx::implementation_simdjson_result_base { - 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 raw() const noexcept; - simdjson_inline simdjson_warn_unused simdjson_result unescape(lasx::ondemand::json_iterator& iter, bool allow_replacement) const noexcept; - simdjson_inline simdjson_warn_unused simdjson_result unescape_wobbly(lasx::ondemand::json_iterator& iter) const noexcept; - }; +template<> +struct simdjson_result : public lasx::implementation_simdjson_result_base { +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 raw() const noexcept; + simdjson_inline char operator[](size_t) const noexcept; + simdjson_inline simdjson_warn_unused simdjson_result unescape(lasx::ondemand::json_iterator &iter, bool allow_replacement) const noexcept; + simdjson_inline simdjson_warn_unused simdjson_result unescape_wobbly(lasx::ondemand::json_iterator &iter) const noexcept; +}; } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H @@ -110805,365 +158320,421 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ #include +#include namespace simdjson { - namespace lasx { - namespace ondemand { +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; +/** + * 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; +/** + * 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; + 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; + /** 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. - * - * @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 iterate(padded_string_view json) & noexcept; + /** + * 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 iterate(padded_string_view json) & noexcept; #ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON - simdjson_warn_unused simdjson_result iterate_allow_incomplete_json(padded_string_view json) & noexcept; + simdjson_warn_unused simdjson_result iterate_allow_incomplete_json(padded_string_view json) & noexcept; #endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const char* json, size_t len, size_t capacity) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const uint8_t* json, size_t len, size_t capacity) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(std::string_view json, size_t capacity) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const std::string& json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(std::string& json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const simdjson_result& json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const simdjson_result& json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(padded_string&& json) & noexcept = delete; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const char *json, size_t len, size_t capacity) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const uint8_t *json, size_t len, size_t capacity) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(std::string_view json, size_t capacity) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const std::string &json) & noexcept; + /** @overload simdjson_result 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 iterate(std::string &json) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const simdjson_result &json) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const simdjson_result &json) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result 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 iterate_raw(padded_string_view json) & noexcept; + /** + * @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 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. - * - * ### 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. - * @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 10MB, which has been a reasonable sweet spot in our tests. - * @param allow_comma_separated (defaults on false) This allows a mode where the documents are - * separated by commas instead of whitespace. It comes with a performance - * penalty because the entire document is indexed at once (and the document must be - * less than 4 GB), and there is no multithreading. In this mode, the batch_size parameter - * is effectively ignored, as it is set to at least the document size. - * @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 iterate_many(const uint8_t* buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ - inline simdjson_result iterate_many(const char* buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ - inline simdjson_result iterate_many(const std::string& s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - inline simdjson_result iterate_many(const std::string&& s, size_t batch_size, bool allow_comma_separated = false) = delete;// unsafe - /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ - inline simdjson_result iterate_many(const padded_string& s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; - inline simdjson_result iterate_many(const padded_string&& s, size_t batch_size, bool allow_comma_separated = false) = delete;// unsafe + /** + * 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 10MB, which has been a reasonable sweet spot in our tests. + * @param allow_comma_separated (defaults on false) This allows a mode where the documents are + * separated by commas instead of whitespace. It comes with a performance + * penalty because the entire document is indexed at once (and the document must be + * less than 4 GB), and there is no multithreading. In this mode, the batch_size parameter + * is effectively ignored, as it is set to at least the document size. + * @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 iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(padded_string_view json, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_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 iterate_many(std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @private We do not want to allow implicit conversion from C string to std::string. */ + simdjson_result iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete; - /** @private We do not want to allow implicit conversion from C string to std::string. */ - simdjson_result iterate_many(const char* buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete; + /** 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; - /** 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; - /** - * 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 unescape(raw_json_string in, uint8_t *&dst, bool allow_replacement = false) const 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 }; -#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 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 unescape_wobbly(raw_json_string in, uint8_t*& dst) 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 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; + /** + * 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 - private: - /** @private [for benchmarking access] The implementation to use */ - std::unique_ptr implementation{}; - size_t _capacity{ 0 }; - size_t _max_capacity; - size_t _max_depth{ DEFAULT_MAX_DEPTH }; - std::unique_ptr string_buf{}; + /** + * 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: + friend bool release_parser(); + friend ondemand::parser& get_parser(); + /** Get the thread-local parser instance, allocates it if needed */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_parser_instance(); + /** Get the thread-local parser instance, it might be null */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_threadlocal_parser_if_exists(); + /** @private [for benchmarking access] The implementation to use */ + std::unique_ptr implementation{}; + size_t _capacity{0}; + size_t _max_capacity; + size_t _max_depth{DEFAULT_MAX_DEPTH}; + std::unique_ptr string_buf{}; + #if SIMDJSON_DEVELOPMENT_CHECKS - std::unique_ptr start_positions{}; + std::unique_ptr start_positions{}; #endif - friend class json_iterator; - friend class document_stream; - }; + friend class json_iterator; + friend class document_stream; +}; - } // namespace ondemand - } // namespace lasx +} // namespace ondemand +} // namespace lasx } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public lasx::implementation_simdjson_result_base { - 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; - }; +template<> +struct simdjson_result : public lasx::implementation_simdjson_result_base { +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 @@ -111180,211 +158751,275 @@ namespace simdjson { /* 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 */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace ondemand { +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; +/** + * 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. - */ - simdjson_inline simdjson_result begin() noexcept; - /** - * Sentinel representing the end of the array. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result 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. - * - * To check that an array is empty, it is more performant to use - * the is_empty() method. - */ - simdjson_inline simdjson_result 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 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 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 at_pointer(std::string_view json_pointer) noexcept; + /** + * Begin array iteration. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result begin() noexcept; + /** + * Sentinel representing the end of the array. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result 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 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 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 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 at_pointer(std::string_view json_pointer) noexcept; - /** - * Get the value associated with the given JSONPath expression. We only support - * JSONPath queries that trivially convertible to JSON Pointer queries: key - * names and array indices. - * - * https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00 - * - * @return The value associated with the given JSONPath expression, or: - * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - */ - inline simdjson_result at_path(std::string_view json_path) noexcept; + /** + * 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 at_path(std::string_view json_path) noexcept; - /** - * Consumes the array and returns a string_view instance corresponding to the - * array as represented in JSON. It points inside the original document. - */ - simdjson_inline simdjson_result raw_json() noexcept; + /** + * Get all values matching the given JSONPath expression with wildcard support. + * Supports wildcard patterns like "[*]" to match all array elements. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern + */ + inline simdjson_result> at_path_with_wildcard(std::string_view json_path) 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 at(size_t index) noexcept; - protected: - /** - * Go to the end of the array, no matter where you are right now. - */ - simdjson_inline error_code consume() 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 raw_json() 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 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 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 started(value_iterator& iter) 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 at(size_t index) 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; +#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 + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + 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 + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(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; - /** - * Iterator marking current position. - * - * iter.is_alive() == false indicates iteration is complete. - */ - value_iterator iter{}; + /** + * 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 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 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 started(value_iterator &iter) noexcept; - friend class value; - friend class document; - friend struct simdjson_result; - friend struct simdjson_result; - friend class array_iterator; - }; + /** + * 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; - } // namespace ondemand - } // namespace lasx + /** + * Iterator marking current position. + * + * iter.is_alive() == false indicates iteration is complete. + */ + value_iterator iter{}; + + friend class value; + friend class document; + friend struct simdjson_result; + friend struct simdjson_result; + friend class array_iterator; +}; + +} // namespace ondemand +} // namespace lasx } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public lasx::implementation_simdjson_result_base { - 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; +template<> +struct simdjson_result : public lasx::implementation_simdjson_result_base { +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 begin() noexcept; - simdjson_inline simdjson_result end() noexcept; - inline simdjson_result count_elements() & noexcept; - inline simdjson_result is_empty() & noexcept; - inline simdjson_result reset() & noexcept; - simdjson_inline simdjson_result at(size_t index) noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - simdjson_inline simdjson_result raw_json() noexcept; + simdjson_inline simdjson_result begin() noexcept; + simdjson_inline simdjson_result end() noexcept; + inline simdjson_result count_elements() & noexcept; + inline simdjson_result is_empty() & noexcept; + inline simdjson_result reset() & noexcept; + simdjson_inline simdjson_result at(size_t index) noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h - }; + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(out) ); + } + return SUCCESS; + } +#endif // SIMDJSON_SUPPORTS_CONCEPTS +}; } // namespace simdjson @@ -111396,6 +159031,7 @@ namespace simdjson { /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ /* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H */ +/* amalgamation skipped (editor-only): #include */ /* 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" */ @@ -111403,87 +159039,109 @@ namespace simdjson { namespace simdjson { - namespace lasx { - namespace ondemand { +namespace lasx { +namespace ondemand { - /** - * A forward-only JSON array. - * - * This is an input_iterator, meaning: - * - It is forward-only - * - * must be called exactly once per element. - * - ++ must be called exactly once in between each * (*, ++, *, ++, * ...) - */ - class array_iterator { - public: - /** Create a new, invalid array iterator. */ - simdjson_inline array_iterator() noexcept = default; +/** + * 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; + using difference_type = std::ptrdiff_t; + using pointer = void; + using reference = value_type; - // - // Iterator interface - // + /** Create a new, invalid array iterator. */ + simdjson_inline array_iterator() noexcept = default; - /** - * Get the current element. - * - * Part of the std::iterator interface. - */ - simdjson_inline simdjson_result 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; + // + // Iterator interface + // - private: - value_iterator iter{}; + /** + * Get the current element. + * + * Part of the std::iterator interface. + */ + simdjson_inline simdjson_result + 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; - simdjson_inline array_iterator(const value_iterator& iter) noexcept; + /** + * Check if the array is at the end. + */ + simdjson_warn_unused simdjson_inline bool at_end() const noexcept; - friend class array; - friend class value; - friend struct simdjson_result; - }; +private: +#if SIMDJSON_DEVELOPMENT_CHECKS + bool has_been_referenced{false}; +#endif + value_iterator iter{}; - } // namespace ondemand - } // namespace lasx + simdjson_inline array_iterator(const value_iterator &iter) noexcept; + + friend class array; + friend class value; + friend struct simdjson_result; +}; + +} // namespace ondemand +} // namespace lasx } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public lasx::implementation_simdjson_result_base { - public: - 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; +template<> +struct simdjson_result : public lasx::implementation_simdjson_result_base { + using iterator_category = std::input_iterator_tag; + using value_type = simdjson_result; + using difference_type = std::ptrdiff_t; + using pointer = void; + using reference = value_type; - // - // Iterator interface - // + 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; - simdjson_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. - simdjson_inline bool operator==(const simdjson_result&) const noexcept; - simdjson_inline bool operator!=(const simdjson_result&) const noexcept; - simdjson_inline simdjson_result& operator++() noexcept; - }; + // + // Iterator interface + // + + simdjson_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline bool operator==(const simdjson_result &) const noexcept; + simdjson_inline bool operator!=(const simdjson_result &) const noexcept; + simdjson_inline simdjson_result &operator++() noexcept; + + simdjson_warn_unused simdjson_inline bool at_end() const noexcept; +}; } // namespace simdjson @@ -111497,839 +159155,1050 @@ namespace simdjson { /* 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 */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + namespace simdjson { - namespace lasx { - namespace ondemand { +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; +/** + * 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 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 get_object() & noexcept; - /** - * Cast this JSON value to an unsigned integer. - * - * @returns A signed 64-bit integer. - * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. - */ - simdjson_inline simdjson_result get_uint64() noexcept; - /** - * Cast this JSON value (inside string) to an unsigned integer. - * - * @returns A signed 64-bit integer. - * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. - */ - simdjson_inline simdjson_result 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 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 get_int64_in_string() 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 get_double() noexcept; + /** + * 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 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 get_object() & noexcept; + /** + * Cast this JSON value to an unsigned integer. + * + * @returns A signed 64-bit integer. + * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. + */ + simdjson_inline simdjson_result get_uint64() noexcept; + /** + * Cast this JSON value (inside string) to an unsigned integer. + * + * @returns A signed 64-bit integer. + * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. + */ + simdjson_inline simdjson_result 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 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 get_int64_in_string() 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 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 get_double_in_string() noexcept; - /** - * 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 get_string(bool allow_replacement = false) noexcept; - /** - * 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 - 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 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 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 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 get_value() 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 get_double_in_string() noexcept; + /** + * 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 get_string(bool allow_replacement = false) noexcept; + /** + * 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 + 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 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 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 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 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 is_null() 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 is_null() noexcept; - /** - * Get this value as the given type. - * - * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool - * - * You may use get_double(), get_bool(), get_uint64(), get_int64(), - * 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 simdjson_inline simdjson_result get() & noexcept { - // 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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " 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."); - } - /** @overload template simdjson_result get() & noexcept */ - template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept { - // 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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " 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."); - } + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool + * + * You may use get_double(), get_bool(), get_uint64(), get_int64(), + * 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 + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + /** + * @overload template simdjson_result 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 + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::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(*this).get(); + } - /** - * Get this value as the given type. - * - * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 simdjson_inline error_code get(T& out) & noexcept; - /** @overload template error_code get(T &out) & noexcept */ - template simdjson_deprecated simdjson_inline error_code get(T& out) && noexcept; + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_CONCEPTS + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { + static_assert(!sizeof(T), "The get 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 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, double, or bool."); + static_cast(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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " 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(out); // to get rid of unused errors + return UNINITIALIZED; +#endif // SIMDJSON_SUPPORTS_CONCEPTS + } + + /** @overload template error_code get(T &out) & noexcept */ + template simdjson_deprecated simdjson_inline error_code get(T &out) && noexcept; #if SIMDJSON_EXCEPTIONS - /** - * Cast this JSON value to an instance of type T. The programmer is responsible for - * providing an implementation of get 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 - explicit simdjson_inline operator T() & noexcept(false); - template - explicit simdjson_deprecated simdjson_inline operator T() && noexcept(false); + /** + * Cast this JSON value to an instance of type T. The programmer is responsible for + * providing an implementation of get 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 + explicit simdjson_inline operator T() & noexcept(false); + template + 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 signed 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. - * - * @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); - /** - * 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); + /** + * 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 signed 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. + * + * @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) 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. + */ + 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. + */ + 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. - */ - simdjson_inline simdjson_result 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 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 at(size_t index) & noexcept; - /** - * Begin array iteration. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result begin() & noexcept; - /** - * Sentinel representing the end of the array. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result 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. + */ + simdjson_inline simdjson_result 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 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 at(size_t index) & noexcept; + /** + * Begin array iteration. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result begin() & noexcept; + /** + * Sentinel representing the end of the array. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result end() & noexcept; - /** - * Look up a field by name on an object (order-sensitive). - * - * 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 }`: - * - * ```c++ - * 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 find_field(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result find_field(const char* key) & 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 find_field(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result 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. - * - * It is the default, however, 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 find_field_unordered(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result find_field_unordered(const char* key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](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 find_field_unordered(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result 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). - * - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". - */ - simdjson_inline simdjson_result type() noexcept; + /** + * 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 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 is_scalar() 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 is_scalar() noexcept; - /** - * Checks whether the document is a string. - * - * @returns true if the type is string - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". - */ - simdjson_inline simdjson_result is_string() noexcept; + /** + * Checks whether the document is a string. + * + * @returns true if the type is string + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + */ + simdjson_inline simdjson_result is_string() noexcept; - /** - * Checks whether the document is a negative number. - * - * @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 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 get_number_type() 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 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 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 get_number() 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 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 raw_json_token() 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 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; + /** + * 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 current_location() const noexcept; + /** + * Returns the current location in the document if in bounds. + */ + inline simdjson_result 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 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; + /** + * 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 at_pointer(std::string_view json_pointer) 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 at_pointer(std::string_view json_pointer) noexcept; - /** - * Get the value associated with the given JSONPath expression. We only support - * JSONPath queries that trivially convertible to JSON Pointer queries: key - * names and array indices. - * - * https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00 - * - * Key values are matched exactly, without unescaping or Unicode normalization. - * We do a byte-by-byte comparison. E.g. - * - * const padded_string json = "{\"\\u00E9\":123}"_padded; - * auto doc = parser.iterate(json); - * doc.at_path(".\\u00E9") == 123 - * doc.at_path((const char*)u8".\u00E9") returns an error (NO_SUCH_FIELD) - * - * @return The value associated with the given JSONPath expression, or: - * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - */ - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + /** + * 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 at_path(std::string_view json_path) noexcept; - /** - * Consumes the document and returns a string_view instance corresponding to the - * document as represented in JSON. It points inside the original byte array containing - * the JSON document. - */ - simdjson_inline simdjson_result raw_json() noexcept; - protected: - /** - * Consumes the document. - */ - simdjson_inline error_code consume() noexcept; + /** + * Get all values matching the given JSONPath expression with wildcard support. + * + * Supports wildcard patterns like "$.array[*]" or "$.object.*" to match multiple elements. + * + * This method materializes all matching values into a vector. + * The document will be consumed after this call. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern, or: + * - INVALID_JSON_POINTER if the JSONPath cannot be parsed + * - NO_SUCH_FIELD if a field does not exist + * - INDEX_OUT_OF_BOUNDS if an array index is out of bounds + * - INCORRECT_TYPE if path traversal encounters wrong type + */ + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; - simdjson_inline document(ondemand::json_iterator&& iter) noexcept; - simdjson_inline const uint8_t* text(uint32_t idx) const 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 raw_json() noexcept; - simdjson_inline value_iterator resume_value_iterator() noexcept; - simdjson_inline value_iterator get_root_value_iterator() noexcept; - simdjson_inline simdjson_result start_or_resume_object() noexcept; - static simdjson_inline document start(ondemand::json_iterator&& iter) 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 + requires(std::is_class_v && (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; - // - // Fields - // - json_iterator iter{}; ///< Current position in the document - static constexpr depth_t DOCUMENT_DEPTH = 0; ///< document depth is always 0 + simdjson_inline document(ondemand::json_iterator &&iter) noexcept; + simdjson_inline const uint8_t *text(uint32_t idx) const noexcept; - 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; - }; + simdjson_inline value_iterator resume_value_iterator() noexcept; + simdjson_inline value_iterator get_root_value_iterator() noexcept; + simdjson_inline simdjson_result 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. - */ - 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 get_array() & noexcept; - simdjson_inline simdjson_result get_object() & noexcept; - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result get_value() noexcept; +/** + * 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 get_array() & noexcept; + simdjson_inline simdjson_result get_object() & noexcept; + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result get_value() noexcept; - simdjson_inline simdjson_result is_null() noexcept; - template simdjson_inline simdjson_result get() & noexcept; - simdjson_inline simdjson_result raw_json() noexcept; - simdjson_inline operator document& () const noexcept; + simdjson_inline simdjson_result is_null() noexcept; + template + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + template + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::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(*this).get(); + } + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_CONCEPTS + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { + static_assert(!sizeof(T), "The get 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 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, double, or bool."); + static_cast(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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " 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(out); // to get rid of unused errors + return UNINITIALIZED; +#endif // SIMDJSON_SUPPORTS_CONCEPTS + } + + /** @overload template error_code get(T &out) & noexcept */ + template simdjson_inline error_code get(T &out) && noexcept; + simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (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 - explicit simdjson_inline operator T() noexcept(false); - simdjson_inline operator array() & noexcept(false); - simdjson_inline operator 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 raw_json_string() noexcept(false); - simdjson_inline operator bool() noexcept(false); - simdjson_inline operator value() noexcept(false); + template + explicit simdjson_inline operator T() noexcept(false); + simdjson_inline operator array() & noexcept(false); + simdjson_inline operator 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 raw_json_string() noexcept(false); + simdjson_inline operator bool() noexcept(false); + simdjson_inline operator value() noexcept(false); #endif - simdjson_inline simdjson_result count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) & noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(const char* key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](const char* key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(const char* key) & noexcept; + simdjson_inline simdjson_result count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) & noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result end() & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(const char *key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; - simdjson_inline simdjson_result type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_string() noexcept; + simdjson_inline simdjson_result type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; - simdjson_inline simdjson_result current_location() noexcept; - simdjson_inline int32_t current_depth() const noexcept; - simdjson_inline bool is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - simdjson_inline simdjson_result raw_json_token() noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result current_location() noexcept; + simdjson_inline int32_t current_depth() const noexcept; + simdjson_inline bool is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + simdjson_inline simdjson_result raw_json_token() noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; - private: - document* doc{ nullptr }; - }; - } // namespace ondemand - } // namespace lasx +private: + document *doc{nullptr}; +}; +} // namespace ondemand +} // namespace lasx } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public lasx::implementation_simdjson_result_base { - 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; +template<> +struct simdjson_result : public lasx::implementation_simdjson_result_base { +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 get_array() & noexcept; - simdjson_inline simdjson_result get_object() & noexcept; - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result get_value() noexcept; - simdjson_inline simdjson_result is_null() noexcept; + simdjson_inline simdjson_result get_array() & noexcept; + simdjson_inline simdjson_result get_object() & noexcept; + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result get_value() noexcept; + simdjson_inline simdjson_result is_null() noexcept; - template simdjson_inline simdjson_result get() & noexcept; - template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept; + template simdjson_inline simdjson_result get() & noexcept; + template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept; - template simdjson_inline error_code get(T& out) & noexcept; - template simdjson_inline error_code get(T& out) && noexcept; + template simdjson_inline error_code get(T &out) & noexcept; + template simdjson_inline error_code get(T &out) && noexcept; #if SIMDJSON_EXCEPTIONS - template ::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); - 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); - simdjson_inline operator lasx::ondemand::value() noexcept(false); -#endif - simdjson_inline simdjson_result count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) & noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(const char* key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](const char* key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(const char* key) & noexcept; - simdjson_inline simdjson_result type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_string() noexcept; - simdjson_inline simdjson_result current_location() noexcept; - simdjson_inline int32_t current_depth() const noexcept; - simdjson_inline bool at_end() const noexcept; - simdjson_inline bool is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - /** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */ - simdjson_inline simdjson_result raw_json_token() noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - }; + using lasx::implementation_simdjson_result_base::operator*; + using lasx::implementation_simdjson_result_base::operator->; + template ::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); + 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); + simdjson_inline operator lasx::ondemand::value() noexcept(false); +#endif + simdjson_inline simdjson_result count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) & noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result end() & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(const char *key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + simdjson_inline simdjson_result type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + simdjson_inline simdjson_result current_location() noexcept; + simdjson_inline int32_t current_depth() const noexcept; + simdjson_inline bool at_end() const noexcept; + simdjson_inline bool is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + /** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */ + simdjson_inline simdjson_result raw_json_token() noexcept; + + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION +}; } // namespace simdjson @@ -112338,68 +160207,81 @@ namespace simdjson { namespace simdjson { - template<> - struct simdjson_result : public lasx::implementation_simdjson_result_base { - 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; +template<> +struct simdjson_result : public lasx::implementation_simdjson_result_base { +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 get_array() & noexcept; - simdjson_inline simdjson_result get_object() & noexcept; - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result get_wobbly_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result get_value() noexcept; - simdjson_inline simdjson_result is_null() noexcept; + simdjson_inline simdjson_result get_array() & noexcept; + simdjson_inline simdjson_result get_object() & noexcept; + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result get_value() noexcept; + simdjson_inline simdjson_result is_null() noexcept; + + template simdjson_inline simdjson_result get() & noexcept; + template simdjson_inline simdjson_result get() && noexcept; + + template simdjson_inline error_code get(T &out) & noexcept; + template simdjson_inline error_code get(T &out) && noexcept; #if SIMDJSON_EXCEPTIONS - template ::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); - 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); - simdjson_inline operator lasx::ondemand::value() noexcept(false); + template + 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); + simdjson_inline operator lasx::ondemand::value() noexcept(false); #endif - simdjson_inline simdjson_result count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) & noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(const char* key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](const char* key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(const char* key) & noexcept; - simdjson_inline simdjson_result type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_string() noexcept; - simdjson_inline simdjson_result current_location() noexcept; - simdjson_inline simdjson_result current_depth() const noexcept; - simdjson_inline simdjson_result is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - /** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */ - simdjson_inline simdjson_result raw_json_token() noexcept; + simdjson_inline simdjson_result count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) & noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result end() & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(const char *key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + simdjson_inline simdjson_result type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + simdjson_inline simdjson_result current_location() noexcept; + simdjson_inline simdjson_result current_depth() const noexcept; + simdjson_inline simdjson_result is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + /** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */ + simdjson_inline simdjson_result raw_json_token() noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; - }; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept; +#endif // SIMDJSON_STATIC_REFLECTION +}; } // namespace simdjson @@ -112425,322 +160307,330 @@ namespace simdjson { #endif namespace simdjson { - namespace lasx { - namespace ondemand { +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 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: +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(); + /** + * 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 }; + 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{}; + /** + * We lock using a mutex. + */ + std::mutex locking_mutex{}; + std::condition_variable cond_var{}; - friend class document_stream; - }; + 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. - * - * ```c++ - * 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; +/** + * 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; + simdjson_inline ~document_stream() noexcept; - /** - * Returns the input size in bytes. - */ - inline size_t size_in_bytes() const 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(); - * - */ - inline size_t truncated_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(); + * + */ + inline size_t truncated_bytes() const noexcept; - class iterator { - public: - using value_type = simdjson_result; - using reference = simdjson_result; - using pointer = void; - using difference_type = std::ptrdiff_t; - using iterator_category = std::input_iterator_tag; + class iterator { + public: + using value_type = simdjson_result; + using reference = simdjson_result; + using pointer = void; + 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; + /** + * 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. - * - * 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 + * + * 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. + * + * 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 error of the stream (if any). + */ + inline error_code error() 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; + /** + * Returns whether the iterator is at the end. + */ + inline bool at_end() const noexcept; - friend class document; - friend class document_stream; - friend class json_iterator; - }; + 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; - /** - * 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; + friend class document; + friend class document_stream; + friend class json_iterator; + }; + using iterator = document_stream::iterator; - private: + /** + * 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; - document_stream& operator=(const document_stream&) = delete; // Disallow copying - document_stream(const document_stream& other) = delete; // Disallow copying +private: - /** - * 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) - */ - simdjson_inline document_stream( - ondemand::parser& parser, - const uint8_t* buf, - size_t len, - size_t batch_size, - bool allow_comma_separated - ) noexcept; + document_stream &operator=(const document_stream &) = delete; // Disallow copying + document_stream(const document_stream &other) = delete; // Disallow copying - /** - * Parse the first document in the buffer. Used by begin(), to handle allocation and - * initialization. - */ - inline void start() noexcept; + /** + * 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) + */ + simdjson_inline document_stream( + ondemand::parser &parser, + const uint8_t *buf, + size_t len, + size_t batch_size, + bool allow_comma_separated + ) 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; + /** + * Parse the first document in the buffer. Used by begin(), to handle allocation and + * initialization. + */ + inline void start() noexcept; - /** Move the json_iterator of the document to the location of the next document in the stream. */ - inline void next_document() 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; - /** Get the next document index. */ - inline size_t next_batch_start() const noexcept; + /** Move the json_iterator of the document to the location of the next document in the stream. */ + inline void next_document() 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; + /** Get the next document index. */ + inline size_t next_batch_start() const noexcept; - // Fields - ondemand::parser* parser; - const uint8_t* buf; - size_t len; - size_t batch_size; - bool allow_comma_separated; - /** - * 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{}; + /** Pass the next batch through stage 1 with the given parser. */ + inline error_code run_stage1(ondemand::parser &p, size_t batch_start) noexcept; -#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; + // Fields + ondemand::parser *parser; + const uint8_t *buf; + size_t len; + size_t batch_size; + bool allow_comma_separated; + /** + * 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{}; - inline void load_from_stage1_thread() noexcept; + #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; - /** Start a thread to run stage 1 on the next batch. */ - inline void start_stage1_thread() noexcept; + inline void load_from_stage1_thread() noexcept; - /** Wait for the stage 1 thread to finish and capture the results. */ - inline void finish_stage1_thread() noexcept; + /** Start a thread to run stage 1 on the next batch. */ + inline void start_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 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. - */ - ondemand::parser stage1_thread_parser{}; + /** Wait for the stage 1 thread to finish and capture the results. */ + inline void finish_stage1_thread() noexcept; - friend struct stage1_worker; -#endif // SIMDJSON_THREADS_ENABLED + /** 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 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. + */ + ondemand::parser stage1_thread_parser{}; - friend class parser; - friend class document; - friend class json_iterator; - friend struct simdjson_result; - friend struct simdjson::internal::simdjson_result_base; - }; // document_stream + friend struct stage1_worker; + #endif // SIMDJSON_THREADS_ENABLED - } // namespace ondemand - } // namespace lasx + friend class parser; + friend class document; + friend class json_iterator; + friend struct simdjson_result; + friend struct simdjson::internal::simdjson_result_base; +}; // document_stream + +} // namespace ondemand +} // namespace lasx } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public lasx::implementation_simdjson_result_base { - 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; - }; +template<> +struct simdjson_result : public lasx::implementation_simdjson_result_base { +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 @@ -112759,104 +160649,104 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace ondemand { +namespace lasx { +namespace ondemand { - /** - * A JSON field (key/value pair) in an object. - * - * Returned from object iteration. - * - * Extends from std::pair so you can use C++ algorithms that rely on pairs. - */ - class field : public std::pair { - public: - /** - * Create a new invalid field. - * - * Exists so you can declare a variable and later assign to it before use. - */ - simdjson_inline field() noexcept; +/** + * A JSON field (key/value pair) in an object. + * + * Returned from object iteration. + * + * Extends from std::pair so you can use C++ algorithms that rely on pairs. + */ +class field : public std::pair { +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 unescaped_key(bool allow_replacement = false) 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. 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 - 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; - /** - * Get the field value. - */ - simdjson_inline ondemand::value& value() & noexcept; - /** - * @overload ondemand::value &ondemand::value() & noexcept - */ - simdjson_inline ondemand::value value() && 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 unescaped_key(bool allow_replacement = false) 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. 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 + 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; + /** + * 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 start(value_iterator& parent_iter) noexcept; - static simdjson_inline simdjson_result start(const value_iterator& parent_iter, raw_json_string key) noexcept; - friend struct simdjson_result; - friend class object_iterator; - }; +protected: + simdjson_inline field(raw_json_string key, ondemand::value &&value) noexcept; + static simdjson_inline simdjson_result start(value_iterator &parent_iter) noexcept; + static simdjson_inline simdjson_result start(const value_iterator &parent_iter, raw_json_string key) noexcept; + friend struct simdjson_result; + friend class object_iterator; +}; - } // namespace ondemand - } // namespace lasx +} // namespace ondemand +} // namespace lasx } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public lasx::implementation_simdjson_result_base { - 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; +template<> +struct simdjson_result : public lasx::implementation_simdjson_result_base { +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 unescaped_key(bool allow_replacement = false) noexcept; - template - simdjson_inline error_code unescaped_key(string_type& receiver, bool allow_replacement = false) noexcept; - simdjson_inline simdjson_result key() noexcept; - simdjson_inline simdjson_result key_raw_json_token() noexcept; - simdjson_inline simdjson_result escaped_key() noexcept; - simdjson_inline simdjson_result value() noexcept; - }; + simdjson_inline simdjson_result unescaped_key(bool allow_replacement = false) noexcept; + template + simdjson_inline error_code unescaped_key(string_type &receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result key() noexcept; + simdjson_inline simdjson_result key_raw_json_token() noexcept; + simdjson_inline simdjson_result escaped_key() noexcept; + simdjson_inline simdjson_result value() noexcept; +}; } // namespace simdjson @@ -112871,253 +160761,367 @@ namespace simdjson { /* 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 */ +/* 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 { +namespace lasx { +namespace ondemand { - /** - * 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; +/** + * 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; - simdjson_inline simdjson_result begin() noexcept; - simdjson_inline simdjson_result end() noexcept; - /** - * Look up a field by name on an object (order-sensitive). - * - * 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 }`: - * - * ```c++ - * 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 }`. - * - * 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 find_field(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result find_field(std::string_view key) && noexcept; + /** + * 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 + */ + simdjson_inline simdjson_result begin() noexcept; + simdjson_inline simdjson_result 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"); + * ``` + * 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 find_field(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result 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. - * - * It is the default, however, 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 find_field_unordered(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](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 find_field_unordered(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) && 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("/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 at_pointer(std::string_view json_pointer) 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("/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 at_pointer(std::string_view json_pointer) noexcept; - /** - * Get the value associated with the given JSONPath expression. We only support - * JSONPath queries that trivially convertible to JSON Pointer queries: key - * names and array indices. - * - * @return The value associated with the given JSONPath expression, or: - * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - */ - inline simdjson_result at_path(std::string_view json_path) noexcept; + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + inline simdjson_result at_path(std::string_view json_path) noexcept; - /** - * Reset the iterator so that we are pointing back at the - * beginning of the object. You should still consume values only once even if you - * 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 reset() & 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 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 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 raw_json() noexcept; + /** + * Get all values matching the given JSONPath expression with wildcard support. + * Supports wildcard patterns like ".*" to match all object fields. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern + */ + inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; - protected: - /** - * Go to the end of the object, no matter where you are right now. - */ - simdjson_inline error_code consume() noexcept; - static simdjson_inline simdjson_result start(value_iterator& iter) noexcept; - static simdjson_inline simdjson_result start_root(value_iterator& iter) noexcept; - static simdjson_inline simdjson_result started(value_iterator& iter) noexcept; - static simdjson_inline object resume(const value_iterator& iter) noexcept; - simdjson_inline object(const value_iterator& iter) 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 reset() & 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 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 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 raw_json() noexcept; - simdjson_warn_unused simdjson_inline error_code find_field_raw(const std::string_view key) 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + 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 + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } - value_iterator iter{}; +#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 + requires(std::is_class_v && (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 start(value_iterator &iter) noexcept; + static simdjson_inline simdjson_result start_root(value_iterator &iter) noexcept; + static simdjson_inline simdjson_result started(value_iterator &iter) noexcept; + static simdjson_inline object resume(const value_iterator &iter) noexcept; + simdjson_inline object(const value_iterator &iter) noexcept; - friend class value; - friend class document; - friend struct simdjson_result; - }; + simdjson_warn_unused simdjson_inline error_code find_field_raw(const std::string_view key) noexcept; - } // namespace ondemand - } // namespace lasx + value_iterator iter{}; + + friend class value; + friend class document; + friend struct simdjson_result; +}; + +} // namespace ondemand +} // namespace lasx } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public lasx::implementation_simdjson_result_base { - 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; +template<> +struct simdjson_result : public lasx::implementation_simdjson_result_base { +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 begin() noexcept; - simdjson_inline simdjson_result end() noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) && noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result begin() noexcept; + simdjson_inline simdjson_result end() noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) && noexcept; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + inline simdjson_result reset() noexcept; + inline simdjson_result is_empty() noexcept; + inline simdjson_result count_fields() & noexcept; + inline simdjson_result raw_json() noexcept; + #if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h - inline simdjson_result reset() noexcept; - inline simdjson_result is_empty() noexcept; - inline simdjson_result count_fields() & noexcept; - inline simdjson_result raw_json() noexcept; + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(out) ); + } + return SUCCESS; + } - }; +#if SIMDJSON_STATIC_REFLECTION + // TODO: move this code into object-inl.h + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) noexcept { + if (error()) { return error(); } + return first.extract_into(out); + } +#endif // SIMDJSON_STATIC_REFLECTION +#endif // SIMDJSON_SUPPORTS_CONCEPTS +}; } // namespace simdjson @@ -113135,72 +161139,77 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace ondemand { +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; +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; - // - // Iterator interface - // + // + // Iterator interface + // - // Reads key and value, yielding them to the user. - // MUST ONLY BE CALLED ONCE PER ITERATION. - simdjson_inline simdjson_result 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 ',' - simdjson_inline object_iterator& operator++() noexcept; + // Reads key and value, yielding them to the user. + // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline simdjson_result 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: - /** - * 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{}; +private: +#if SIMDJSON_DEVELOPMENT_CHECKS + bool has_been_referenced{false}; +#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; - friend class object; - }; + simdjson_inline object_iterator(const value_iterator &iter) noexcept; + friend struct simdjson_result; + friend class object; +}; - } // namespace ondemand - } // namespace lasx +} // namespace ondemand +} // namespace lasx } // namespace simdjson namespace simdjson { - template<> - struct simdjson_result : public lasx::implementation_simdjson_result_base { - 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; +template<> +struct simdjson_result : public lasx::implementation_simdjson_result_base { +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 - // + // + // Iterator interface + // - // Reads key and value, yielding them to the user. - simdjson_inline simdjson_result 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&) const noexcept; - // Assumes it's being compared with the end. true if depth >= iter->depth. - simdjson_inline bool operator!=(const simdjson_result&) const noexcept; - // Checks for ']' and ',' - simdjson_inline simdjson_result& operator++() noexcept; - }; + // Reads key and value, yielding them to the user. + simdjson_inline simdjson_result 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 &) const noexcept; + // Assumes it's being compared with the end. true if depth >= iter->depth. + simdjson_inline bool operator!=(const simdjson_result &) const noexcept; + // Checks for ']' and ',' + simdjson_inline simdjson_result &operator++() noexcept; +}; } // namespace simdjson @@ -113216,35 +161225,49 @@ namespace simdjson { /* 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 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 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 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 to_json_string(lasx::ondemand::array& x) noexcept; - inline simdjson_result to_json_string(simdjson_result x); - inline simdjson_result to_json_string(simdjson_result x); - inline simdjson_result to_json_string(simdjson_result x); - inline simdjson_result to_json_string(simdjson_result x); +/** + * 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 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 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 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 to_json_string(lasx::ondemand::array& x) noexcept; +inline simdjson_result to_json_string(simdjson_result x); +inline simdjson_result to_json_string(simdjson_result x); +inline simdjson_result to_json_string(simdjson_result x); +inline simdjson_result to_json_string(simdjson_result 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 + requires(!std::same_as && + !std::same_as && + !std::same_as && + !std::same_as) +inline std::string to_json_string(const T& obj); +#endif + } // namespace simdjson /** @@ -113254,69 +161277,652 @@ namespace simdjson { * Credit: @madhur4127 * See https://github.com/simdjson/simdjson/issues/1768 */ -namespace simdjson { - namespace lasx { - namespace ondemand { +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); +/** + * 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 x); +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result 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); +/** + * 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 x); +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result 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); +/** + * 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&& x); +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& x); #endif - inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::document_reference& value); +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&& x); +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& 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); +/** + * 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 x); +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x); #endif - } - } -} // namespace simdjson::lasx::ondemand +}}} // 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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +#include +#if SIMDJSON_STATIC_REFLECTION +#include +// #include // for std::define_static_string - header not available yet +#endif + +namespace simdjson { + +////////////////////////////// +// Number deserialization +////////////////////////////// + +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + uint64_t x; + SIMDJSON_TRY(val.get_uint64().get(x)); + if (x > (limits::max)()) { + return NUMBER_OUT_OF_RANGE; + } + out = static_cast(x); + return SUCCESS; +} + +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + double x; + SIMDJSON_TRY(val.get_double().get(x)); + out = static_cast(x); + return SUCCESS; +} + +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + 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(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 +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v) { + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + out = T{str}; + return SUCCESS; +} + + +/** + * 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>(). + */ +template +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { + using value_type = typename std::remove_cvref_t::value_type; + static_assert( + deserializable, + "The specified type inside the container must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the container must default constructible."); + lasx::ondemand::array arr; + if constexpr (std::is_same_v, lasx::ondemand::array>) { + arr = val; + } else { + SIMDJSON_TRY(val.get_array().get(arr)); + } + + for (auto v : arr) { + if constexpr (concepts::returns_reference) { + if (auto const err = v.get().get(concepts::emplace_one(out)); + err) { + // If an error occurs, the empty element that we just inserted gets + // removed. We're not using a temp variable because if T is a heavy + // type, we want the valid path to be the fast path and the slow path be + // the path that has errors in it. + if constexpr (requires { out.pop_back(); }) { + static_cast(out.pop_back()); + } + return err; + } + } else { + value_type temp; + if (auto const err = v.get().get(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 +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { + using value_type = typename std::remove_cvref_t::mapped_type; + static_assert( + deserializable, + "The specified value type inside the container must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "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().get(this_value)); + [[maybe_unused]] std::pair 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 +error_code tag_invoke(deserialize_tag, lasx::ondemand::object &obj, T &out) noexcept { + using value_type = typename std::remove_cvref_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 +error_code tag_invoke(deserialize_tag, lasx::ondemand::value &val, T &out) noexcept { + lasx::ondemand::object obj; + SIMDJSON_TRY(val.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} + +template +error_code tag_invoke(deserialize_tag, lasx::ondemand::document &doc, T &out) noexcept { + lasx::ondemand::object obj; + SIMDJSON_TRY(doc.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} + +template +error_code tag_invoke(deserialize_tag, lasx::ondemand::document_reference &doc, T &out) noexcept { + 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. + * + * If constructing T is nothrow, this conversion should be nothrow as well since + * we return MEMALLOC if we're not able to allocate memory instead of throwing + * the error message. + * + * @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 +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::element_type, ValT>) { + using element_type = typename std::remove_cvref_t::element_type; + + // For better error messages, don't use these as constraints on + // the tag_invoke CPO. + static_assert( + deserializable, + "The specified type inside the unique_ptr must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the unique_ptr must default constructible."); + + auto ptr = new (std::nothrow) element_type(); + if (ptr == nullptr) { + return MEMALLOC; + } + SIMDJSON_TRY(val.template get(*ptr)); + out.reset(ptr); + return SUCCESS; +} + +/** + * This CPO (Customization Point Object) will help deserialize into optional types. + */ +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable::value_type, decltype(val)>) { + using value_type = typename std::remove_cvref_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(out.value())); + return SUCCESS; +} + + +#if SIMDJSON_STATIC_REFLECTION + + +template +constexpr bool user_defined_type = (std::is_class_v +&& !std::is_same_v && !std::is_same_v && !concepts::optional_type && +!concepts::appendable_containers); + + +template + requires(user_defined_type && std::is_class_v) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { + lasx::ondemand::object obj; + if constexpr (std::is_same_v, lasx::ondemand::object>) { + obj = val; + } else { + SIMDJSON_TRY(val.get_object().get(obj)); + } + 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)); + if constexpr (concepts::optional_type) { + // for optional members, it's ok if the key is missing + auto error = obj[key].get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + if(error == NO_SUCH_FIELD) { + out.[:mem:].reset(); + continue; + } + return error; + } + } else { + // for non-optional members, the key must be present + SIMDJSON_TRY(obj[key].get(out.[:mem:])); + } + } + }; + return simdjson::SUCCESS; +} + +// Support for enum deserialization - deserialize from string representation using expand approach from P2996R12 +template + requires(std::is_enum_v) +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)); + constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T)); + template for (constexpr auto enum_val : enumerators) { + if (str == std::meta::identifier_of(enum_val)) { + out = [:enum_val:]; + return SUCCESS; + } + }; + + return INCORRECT_TYPE; +#else + // Fallback: deserialize as integer if reflection not available + std::underlying_type_t int_val; + SIMDJSON_TRY(val.get(int_val)); + out = static_cast(int_val); + return SUCCESS; +#endif +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr &out) noexcept { + if (!out) { + out = std::make_unique(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr &out) noexcept { + if (!out) { + out = std::make_shared(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +#endif // SIMDJSON_STATIC_REFLECTION + +//////////////////////////////////////// +// Unique pointers +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + 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 &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + 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 &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_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 &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_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 &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(); + } + int64_t temp; + SIMDJSON_TRY(val.get_int64().get(temp)); + *out = static_cast(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 */ @@ -113324,6 +161930,7 @@ namespace simdjson { /* 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" */ @@ -113333,279 +161940,291 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace ondemand { +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. - // +// +// ### 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 array::array(const value_iterator &_iter) noexcept + : iter{_iter} +{ +} - simdjson_inline simdjson_result 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::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::started(value_iterator& iter) noexcept { - bool has_value; - SIMDJSON_TRY(iter.started_array().get(has_value)); - return array(iter); - } +simdjson_inline simdjson_result 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::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::started(value_iterator &iter) noexcept { + bool has_value; + SIMDJSON_TRY(iter.started_array().get(has_value)); + return array(iter); +} - simdjson_inline simdjson_result array::begin() noexcept { +simdjson_inline simdjson_result array::begin() noexcept { #if SIMDJSON_DEVELOPMENT_CHECKS - if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } + if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } #endif - return array_iterator(iter); - } - simdjson_inline simdjson_result array::end() noexcept { - return array_iterator(iter); - } - simdjson_inline error_code array::consume() noexcept { - auto error = iter.json_iter().skip_child(iter.depth() - 1); - if (error) { iter.abandon(); } - return error; - } + return array_iterator(iter); +} +simdjson_inline simdjson_result 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 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(starting_point), size_t(final_point - starting_point)); - } +simdjson_inline simdjson_result 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(starting_point), size_t(final_point - starting_point)); +} - SIMDJSON_PUSH_DISABLE_WARNINGS - SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING - simdjson_inline simdjson_result array::count_elements() & 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 array after counting the number of elements. - iter.reset_array(); - return count; - } - SIMDJSON_POP_DISABLE_WARNINGS +SIMDJSON_PUSH_DISABLE_WARNINGS +SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING +simdjson_inline simdjson_result array::count_elements() & 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 array after counting the number of elements. + iter.reset_array(); + return count; +} +SIMDJSON_POP_DISABLE_WARNINGS - simdjson_inline simdjson_result array::is_empty() & noexcept { - bool is_not_empty; - auto error = iter.reset_array().get(is_not_empty); - if (error) { return error; } - return !is_not_empty; - } +simdjson_inline simdjson_result array::is_empty() & noexcept { + bool is_not_empty; + auto error = iter.reset_array().get(is_not_empty); + if(error) { return error; } + return !is_not_empty; +} - inline simdjson_result array::reset() & noexcept { - return iter.reset_array(); - } +inline simdjson_result array::reset() & noexcept { + return iter.reset_array(); +} - inline simdjson_result array::at_pointer(std::string_view json_pointer) noexcept { - if (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; } +inline simdjson_result array::at_pointer(std::string_view json_pointer) noexcept { + if (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; } - // Read the array index - size_t array_index = 0; - size_t i; - for (i = 0; i < json_pointer.length() && json_pointer[i] != '/'; i++) { - uint8_t digit = uint8_t(json_pointer[i] - '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; } - array_index = array_index * 10 + digit; - } + // Read the array index + size_t array_index = 0; + size_t i; + for (i = 0; i < json_pointer.length() && json_pointer[i] != '/'; i++) { + uint8_t digit = uint8_t(json_pointer[i] - '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; } + array_index = array_index*10 + digit; + } - // 0 followed by other digits is invalid - if (i > 1 && json_pointer[0] == '0') { return INVALID_JSON_POINTER; } // "JSON pointer array index has other characters after 0" + // 0 followed by other digits is invalid + if (i > 1 && json_pointer[0] == '0') { return INVALID_JSON_POINTER; } // "JSON pointer array index has other characters after 0" - // Empty string is invalid; so is a "/" with no digits before it - if (i == 0) { return INVALID_JSON_POINTER; } // "Empty string in JSON pointer array index" - // Get the child - auto child = at(array_index); - // If there is an error, it ends here - if (child.error()) { - return child; - } + // Empty string is invalid; so is a "/" with no digits before it + if (i == 0) { return INVALID_JSON_POINTER; } // "Empty string in JSON pointer array index" + // 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; - } + // 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 std::string json_path_to_pointer_conversion(std::string_view json_path) { - if (json_path.empty() || (json_path.front() != '.' && - json_path.front() != '[')) { - return "-1"; // This is just a sentinel value, the caller should check for this and return an error. - } +inline simdjson_result array::at_path(std::string_view json_path) noexcept { + auto json_pointer = json_path_to_pointer_conversion(json_path); + if (json_pointer == "-1") { return INVALID_JSON_POINTER; } + return at_pointer(json_pointer); +} - std::string result; - // Reserve space to reduce allocations, adjusting for potential increases due - // to escaping. - result.reserve(json_path.size() * 2); +inline simdjson_result> array::at_path_with_wildcard(std::string_view json_path) noexcept { + std::vector result; - size_t i = 0; + 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; + // Wildcard case + if(key=="*"){ + for(auto element: *this){ - 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; - } + if(element.error()){ + return element.error(); + } - return result; - } + if(remaining_path.empty()){ + // Use value_unsafe() because we've already checked for errors above. + // The 'element' is a simdjson_result wrapper, and we need to extract + // the underlying value. value_unsafe() is safe here because error() returned false. + result.push_back(std::move(element).value_unsafe()); - inline simdjson_result array::at_path(std::string_view json_path) noexcept { - auto json_pointer = json_path_to_pointer_conversion(json_path); - if (json_pointer == "-1") { return INVALID_JSON_POINTER; } - return at_pointer(json_pointer); - } + }else{ + auto nested_result = element.at_path_with_wildcard(remaining_path); - simdjson_inline simdjson_result array::at(size_t index) noexcept { - size_t i = 0; - for (auto value : *this) { - if (i == index) { return value; } - i++; - } - return INDEX_OUT_OF_BOUNDS; - } + if(nested_result.error()){ + return nested_result.error(); + } + // Same logic as above. + std::vector nested_matches = std::move(nested_result).value_unsafe(); - } // namespace ondemand - } // namespace lasx + result.insert(result.end(), + std::make_move_iterator(nested_matches.begin()), + std::make_move_iterator(nested_matches.end())); + } + } + return result; + }else{ + // Specific index case in which we access the element at the given index + size_t idx=0; + + for(char c:key){ + if(c < '0' || c > '9'){ + return INVALID_JSON_POINTER; + } + idx = idx*10 + (c - '0'); + } + + auto element = at(idx); + + if(element.error()){ + return element.error(); + } + + if(remaining_path.empty()){ + result.push_back(std::move(element).value_unsafe()); + return result; + }else{ + return element.at_path_with_wildcard(remaining_path); + } + } +} + +simdjson_inline simdjson_result array::at(size_t index) noexcept { + 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::simdjson_result( - lasx::ondemand::array&& value - ) noexcept - : implementation_simdjson_result_base( - std::forward(value) - ) - { - } - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept - : implementation_simdjson_result_base(error) - { - } +simdjson_inline simdjson_result::simdjson_result( + lasx::ondemand::array &&value +) noexcept + : implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept + : implementation_simdjson_result_base(error) +{ +} - simdjson_inline simdjson_result simdjson_result::begin() noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() noexcept { - if (error()) { return error(); } - return first.end(); - } - simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); - } - simdjson_inline simdjson_result simdjson_result::is_empty() & noexcept { - if (error()) { return error(); } - return first.is_empty(); - } - simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } - simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { return error(); } - return first.at_path(json_path); - } - simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { - if (error()) { return error(); } - return first.raw_json(); - } +simdjson_inline simdjson_result simdjson_result::begin() noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() noexcept { + if (error()) { return error(); } + return first.end(); +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::is_empty() & noexcept { + if (error()) { return error(); } + return first.is_empty(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path(json_path); +} +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path_with_wildcard(json_path); +} +simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { + if (error()) { return error(); } + return first.raw_json(); +} } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H @@ -113623,74 +162242,661 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace ondemand { +namespace lasx { +namespace ondemand { - simdjson_inline array_iterator::array_iterator(const value_iterator& _iter) noexcept - : iter{ _iter } - {} +simdjson_inline array_iterator::array_iterator(const value_iterator &_iter) noexcept + : iter{_iter} +{} - simdjson_inline simdjson_result array_iterator::operator*() noexcept { - 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 { - 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 simdjson_result 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; +} - } // namespace ondemand - } // namespace lasx +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::simdjson_result( - lasx::ondemand::array_iterator&& value - ) noexcept - : lasx::implementation_simdjson_result_base(std::forward(value)) - { - first.iter.assert_is_valid(); - } - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : lasx::implementation_simdjson_result_base({}, error) - { - } - - simdjson_inline simdjson_result simdjson_result::operator*() noexcept { - if (error()) { return error(); } - return *first; - } - simdjson_inline bool simdjson_result::operator==(const simdjson_result& other) const noexcept { - if (!first.iter.is_valid()) { return !error(); } - return first == other.first; - } - simdjson_inline bool simdjson_result::operator!=(const simdjson_result& other) const noexcept { - if (!first.iter.is_valid()) { return error(); } - return first != other.first; - } - simdjson_inline simdjson_result& simdjson_result::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 simdjson_result::simdjson_result( + lasx::ondemand::array_iterator &&value +) noexcept + : lasx::implementation_simdjson_result_base(std::forward(value)) +{ + first.iter.assert_is_valid(); +} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : lasx::implementation_simdjson_result_base({}, error) +{ +} +simdjson_inline simdjson_result simdjson_result::operator*() noexcept { + if (error()) { return error(); } + return *first; +} +simdjson_inline bool simdjson_result::operator==(const simdjson_result &other) const noexcept { + if (!first.iter.is_valid()) { return !error(); } + return first == other.first; +} +simdjson_inline bool simdjson_result::operator!=(const simdjson_result &other) const noexcept { + if (!first.iter.is_valid()) { return error(); } + return first != other.first; +} +simdjson_inline simdjson_result &simdjson_result::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::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 */ + +namespace simdjson { +namespace lasx { +namespace ondemand { + +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 value::get_array() noexcept { + return array::start(iter); +} +simdjson_inline simdjson_result value::get_object() noexcept { + return object::start(iter); +} +simdjson_inline simdjson_result value::start_or_resume_object() noexcept { + if (iter.at_start()) { + return get_object(); + } else { + return object::resume(iter); + } +} + +simdjson_inline simdjson_result value::get_raw_json_string() noexcept { + return iter.get_raw_json_string(); +} +simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { + return iter.get_string(allow_replacement); +} +template +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 value::get_wobbly_string() noexcept { + return iter.get_wobbly_string(); +} +simdjson_inline simdjson_result value::get_double() noexcept { + return iter.get_double(); +} +simdjson_inline simdjson_result value::get_double_in_string() noexcept { + return iter.get_double_in_string(); +} +simdjson_inline simdjson_result value::get_uint64() noexcept { + return iter.get_uint64(); +} +simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { + return iter.get_uint64_in_string(); +} +simdjson_inline simdjson_result value::get_int64() noexcept { + return iter.get_int64(); +} +simdjson_inline simdjson_result value::get_int64_in_string() noexcept { + return iter.get_int64_in_string(); +} +simdjson_inline simdjson_result value::get_bool() noexcept { + return iter.get_bool(); +} +simdjson_inline simdjson_result value::is_null() noexcept { + return iter.is_null(); +} + +template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result 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); } +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(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(bool& out) noexcept { return get_bool().get(out); } + +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline value::operator T() noexcept(false) { + return get(); +} +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 value::begin() & noexcept { + return get_array().begin(); +} +simdjson_inline simdjson_result value::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result value::count_elements() & noexcept { + simdjson_result 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 value::count_fields() & noexcept { + simdjson_result answer; + auto a = get_object(); + answer = a.count_fields(); + iter.move_at_start(); + return answer; +} +simdjson_inline simdjson_result value::at(size_t index) noexcept { + auto a = get_array(); + return a.at(index); +} + +simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result value::find_field(const char *key) noexcept { + return start_or_resume_object().find_field(key); +} + +simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result value::find_field_unordered(const char *key) noexcept { + return start_or_resume_object().find_field_unordered(key); +} + +simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { + return start_or_resume_object()[key]; +} +simdjson_inline simdjson_result value::operator[](const char *key) noexcept { + return start_or_resume_object()[key]; +} + +simdjson_inline simdjson_result value::type() noexcept { + return iter.type(); +} + +simdjson_inline simdjson_result 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 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 value::is_integer() noexcept { + return iter.is_integer(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { + return iter.get_number_type(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { + return iter.get_number(); +} + +simdjson_inline std::string_view value::raw_json_token() noexcept { + return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); +} + +simdjson_inline simdjson_result 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 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::at_pointer(std::string_view json_pointer) noexcept { + 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::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; + } +} + +inline simdjson_result> value::at_path_with_wildcard(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_with_wildcard(json_path); + case json_type::object: + return (*this).get_object().at_path_with_wildcard(json_path); + default: + return INVALID_JSON_POINTER; + } +} + +} // namespace ondemand +} // namespace lasx +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + lasx::ondemand::value &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base(error) +{ +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + if (error()) { return error(); } + return {}; +} + +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) noexcept { + if (error()) { return error(); } + return first.find_field(key); +} + +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} + +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) noexcept { + if (error()) { return error(); } + return first[key]; +} + +simdjson_inline simdjson_result simdjson_result::get_array() noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} + +template<> simdjson_inline error_code simdjson_result::get(lasx::ondemand::value &out) noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} + +template simdjson_inline simdjson_result simdjson_result::get() noexcept { + if (error()) { return error(); } + return first.get(); +} +template simdjson_inline error_code simdjson_result::get(T &out) noexcept { + if (error()) { return error(); } + return first.get(out); +} + +template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { + if (error()) { return error(); } + return std::move(first); +} + +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline simdjson_result::operator T() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first.get(); +} +simdjson_inline simdjson_result::operator lasx::ondemand::array() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator lasx::ondemand::object() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator lasx::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { + if (error()) { return error(); } + return first.raw_json(); +} + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { + if (error()) { return error(); } + return first.current_depth(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer( + std::string_view json_pointer) noexcept { + if (error()) { + return error(); + } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + +inline simdjson_result> simdjson_result::at_path_with_wildcard( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path_with_wildcard(json_path); +} + +} // 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 @@ -113703,911 +162909,1055 @@ namespace simdjson { /* 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 */ namespace simdjson { - namespace lasx { - namespace ondemand { +namespace lasx { +namespace ondemand { - simdjson_inline document::document(ondemand::json_iterator&& _iter) noexcept - : iter{ std::forward(_iter) } - { - logger::log_start_value(iter, "document"); - } +simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept + : iter{std::forward(_iter)} +{ + logger::log_start_value(iter, "document"); +} - simdjson_inline document document::start(json_iterator&& iter) noexcept { - return document(std::forward(iter)); - } +simdjson_inline document document::start(json_iterator &&iter) noexcept { + return document(std::forward(iter)); +} - inline void document::rewind() noexcept { - iter.rewind(); - } +inline void document::rewind() noexcept { + iter.rewind(); +} - inline std::string document::to_debug_string() noexcept { - return iter.to_string(); - } +inline std::string document::to_debug_string() noexcept { + return iter.to_string(); +} - inline simdjson_result document::current_location() const noexcept { - return iter.current_location(); - } +inline simdjson_result document::current_location() const noexcept { + return iter.current_location(); +} - inline int32_t document::current_depth() const noexcept { - return iter.depth(); - } +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::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 document::start_or_resume_object() noexcept { - if (iter.at_root()) { - return get_object(); - } - else { - return object::resume(resume_value_iterator()); - } - } - simdjson_inline simdjson_result document::get_value() noexcept { - // Make sure we start any arrays or objects before returning, so that start_root_() - // gets called. +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 document::start_or_resume_object() noexcept { + if (iter.at_root()) { + return get_object(); + } else { + return object::resume(resume_value_iterator()); + } +} +simdjson_inline simdjson_result document::get_value() noexcept { + // Make sure we start any arrays or objects before returning, so that start_root_() + // 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. + // 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; } + 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 document::get_array() & noexcept { - auto value = get_root_value_iterator(); - return array::start_root(value); - } - simdjson_inline simdjson_result document::get_object() & noexcept { - auto value = get_root_value_iterator(); - return object::start_root(value); - } + // 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 document::get_array() & noexcept { + auto value = get_root_value_iterator(); + return array::start_root(value); +} +simdjson_inline simdjson_result 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. - */ +/** + * 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 document::get_uint64() noexcept { - return get_root_value_iterator().get_root_uint64(true); - } - simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { - return get_root_value_iterator().get_root_uint64_in_string(true); - } - simdjson_inline simdjson_result document::get_int64() noexcept { - return get_root_value_iterator().get_root_int64(true); - } - simdjson_inline simdjson_result document::get_int64_in_string() noexcept { - return get_root_value_iterator().get_root_int64_in_string(true); - } - simdjson_inline simdjson_result document::get_double() noexcept { - return get_root_value_iterator().get_root_double(true); - } - simdjson_inline simdjson_result document::get_double_in_string() noexcept { - return get_root_value_iterator().get_root_double_in_string(true); - } - simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { - return get_root_value_iterator().get_root_string(true, allow_replacement); - } - template - 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 document::get_wobbly_string() noexcept { - return get_root_value_iterator().get_root_wobbly_string(true); - } - simdjson_inline simdjson_result document::get_raw_json_string() noexcept { - return get_root_value_iterator().get_root_raw_json_string(true); - } - simdjson_inline simdjson_result document::get_bool() noexcept { - return get_root_value_iterator().get_root_bool(true); - } - simdjson_inline simdjson_result document::is_null() noexcept { - return get_root_value_iterator().is_root_null(true); - } +simdjson_inline simdjson_result document::get_uint64() noexcept { + return get_root_value_iterator().get_root_uint64(true); +} +simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { + return get_root_value_iterator().get_root_uint64_in_string(true); +} +simdjson_inline simdjson_result document::get_int64() noexcept { + return get_root_value_iterator().get_root_int64(true); +} +simdjson_inline simdjson_result document::get_int64_in_string() noexcept { + return get_root_value_iterator().get_root_int64_in_string(true); +} +simdjson_inline simdjson_result document::get_double() noexcept { + return get_root_value_iterator().get_root_double(true); +} +simdjson_inline simdjson_result document::get_double_in_string() noexcept { + return get_root_value_iterator().get_root_double_in_string(true); +} +simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { + return get_root_value_iterator().get_root_string(true, allow_replacement); +} +template +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 document::get_wobbly_string() noexcept { + return get_root_value_iterator().get_root_wobbly_string(true); +} +simdjson_inline simdjson_result document::get_raw_json_string() noexcept { + return get_root_value_iterator().get_root_raw_json_string(true); +} +simdjson_inline simdjson_result document::get_bool() noexcept { + return get_root_value_iterator().get_root_bool(true); +} +simdjson_inline simdjson_result document::is_null() noexcept { + return get_root_value_iterator().is_root_null(true); +} - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } - template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } - template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } - template<> simdjson_deprecated simdjson_inline simdjson_result 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); } +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(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(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_inline error_code document::get(T& out) & noexcept { - return get().get(out); - } - template simdjson_deprecated simdjson_inline error_code document::get(T& out) && noexcept { - return std::forward(*this).get().get(out); - } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_value(); } #if SIMDJSON_EXCEPTIONS - template - simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } - template - simdjson_inline document::operator T() & noexcept(false) { return get(); } - 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) { return get_string(false); } - simdjson_inline document::operator raw_json_string() noexcept(false) { 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(); } +template +simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } +template +simdjson_inline document::operator T() & noexcept(false) { return get(); } +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 document::count_elements() & noexcept { - auto a = get_array(); - simdjson_result 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 document::count_elements() & noexcept { + auto a = get_array(); + simdjson_result 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 document::count_fields() & noexcept { + auto a = get_object(); + simdjson_result 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 document::at(size_t index) & noexcept { + auto a = get_array(); + return a.at(index); +} +simdjson_inline simdjson_result document::begin() & noexcept { + return get_array().begin(); +} +simdjson_inline simdjson_result document::end() & noexcept { + return {}; +} + +simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field(const char *key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::find_field_unordered(const char *key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { + return start_or_resume_object()[key]; +} +simdjson_inline simdjson_result 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 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(starting_point), size_t(final_point - starting_point)); +} + +simdjson_inline simdjson_result document::type() noexcept { + return get_root_value_iterator().type(); +} + +simdjson_inline simdjson_result 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 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 document::is_integer() noexcept { + return get_root_value_iterator().is_root_integer(true); +} + +simdjson_inline simdjson_result document::get_number_type() noexcept { + return get_root_value_iterator().get_root_number_type(true); +} + +simdjson_inline simdjson_result document::get_number() noexcept { + return get_root_value_iterator().get_root_number(true); +} + + +simdjson_inline simdjson_result document::raw_json_token() noexcept { + auto _iter = get_root_value_iterator(); + return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); +} + +simdjson_inline simdjson_result 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 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; + } +} + +simdjson_inline simdjson_result> document::at_path_with_wildcard(std::string_view json_path) noexcept { + rewind(); // Rewind the document each time 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().at_path_with_wildcard(json_path); + case json_type::object: + return (*this).get_object().at_path_with_wildcard(json_path); + default: + return INVALID_JSON_POINTER; + } +} + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (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) { + // 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; } - simdjson_inline simdjson_result document::count_fields() & noexcept { - auto a = get_object(); - simdjson_result 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 document::at(size_t index) & noexcept { - auto a = get_array(); - return a.at(index); - } - simdjson_inline simdjson_result document::begin() & noexcept { - return get_array().begin(); - } - simdjson_inline simdjson_result document::end() & noexcept { - return {}; - } - - simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { - return start_or_resume_object().find_field(key); - } - simdjson_inline simdjson_result document::find_field(const char* key) & noexcept { - return start_or_resume_object().find_field(key); - } - simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { - return start_or_resume_object().find_field_unordered(key); - } - simdjson_inline simdjson_result document::find_field_unordered(const char* key) & noexcept { - return start_or_resume_object().find_field_unordered(key); - } - simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { - return start_or_resume_object()[key]; - } - simdjson_inline simdjson_result document::operator[](const char* key) & noexcept { - return start_or_resume_object()[key]; - } - - simdjson_inline error_code document::consume() noexcept { - auto error = iter.skip_child(0); - if (error) { iter.abandon(); } - return error; - } - - simdjson_inline simdjson_result 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(starting_point), size_t(final_point - starting_point)); - } - - simdjson_inline simdjson_result document::type() noexcept { - return get_root_value_iterator().type(); - } - - simdjson_inline simdjson_result document::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 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 document::is_integer() noexcept { - return get_root_value_iterator().is_root_integer(true); - } - - simdjson_inline simdjson_result document::get_number_type() noexcept { - return get_root_value_iterator().get_root_number_type(true); - } - - simdjson_inline simdjson_result document::get_number() noexcept { - return get_root_value_iterator().get_root_number(true); - } - - - simdjson_inline simdjson_result document::raw_json_token() noexcept { - auto _iter = get_root_value_iterator(); - return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); - } - - simdjson_inline simdjson_result 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 document::at_path(std::string_view json_path) noexcept { - rewind(); // Rewind the document each time at_pointer is called - if (json_path.empty()) { - return this->get_value(); - } - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) { - case json_type::array: - return (*this).get_array().at_path(json_path); - case json_type::object: - return (*this).get_object().at_path(json_path); - default: - return INVALID_JSON_POINTER; - } - } - - } // namespace ondemand - } // namespace lasx -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result( - lasx::ondemand::document&& value - ) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) - { - } - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept : - implementation_simdjson_result_base( - error - ) - { - } - simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); - } - simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); - } - simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; - } - simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() & noexcept { - return {}; - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char* key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](const char* key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::find_field(const char* key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { - if (error()) { return error(); } - return first.get_array(); - } - simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { - if (error()) { return error(); } - return first.get_object(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); - } - simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); - } - simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); - } - template - simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(receiver, allow_replacement); - } - simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); - } - simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); - } - simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); - } - simdjson_inline simdjson_result simdjson_result::get_value() noexcept { - if (error()) { return error(); } - return first.get_value(); - } - simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); - } - - template - simdjson_inline simdjson_result simdjson_result::get() & noexcept { - if (error()) { return error(); } - return first.get(); - } - template - simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { - if (error()) { return error(); } - return std::forward(first).get(); - } - template - simdjson_inline error_code simdjson_result::get(T& out) & noexcept { - if (error()) { return error(); } - return first.get(out); - } - template - simdjson_inline error_code simdjson_result::get(T& out) && noexcept { - if (error()) { return error(); } - return std::forward(first).get(out); - } - - template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; - template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { - if (error()) { return error(); } - return std::forward(first); - } - template<> simdjson_inline error_code simdjson_result::get(lasx::ondemand::document& out) & noexcept = delete; - template<> simdjson_inline error_code simdjson_result::get(lasx::ondemand::document& out) && noexcept { - if (error()) { return error(); } - out = std::forward(first); - return SUCCESS; - } - - simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); - } - - simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); - } - - simdjson_inline simdjson_result simdjson_result::is_string() noexcept { - if (error()) { return error(); } - return first.is_string(); - } - - simdjson_inline bool simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); - } - - simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); - } - - simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); - } - - simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); - } - - -#if SIMDJSON_EXCEPTIONS - template ::value == false>::type> - simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator lasx::ondemand::array() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator lasx::ondemand::object() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator lasx::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator lasx::ondemand::value() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } -#endif - - - simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); - } - - simdjson_inline bool simdjson_result::at_end() const noexcept { - if (error()) { return error(); } - return first.at_end(); - } - - - simdjson_inline int32_t simdjson_result::current_depth() const noexcept { - if (error()) { return error(); } - return first.current_depth(); - } - - simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); - } - - simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } - - simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { return error(); } - return first.at_path(json_path); - } - -} // namespace simdjson - - -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 document_reference::get_array() & noexcept { return doc->get_array(); } - simdjson_inline simdjson_result 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 document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } - simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } - simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } - simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } - simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } - simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } - simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } - template - 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 document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } - simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } - simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } - simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } - simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } - template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } -#if SIMDJSON_EXCEPTIONS - template - simdjson_inline document_reference::operator T() noexcept(false) { return get(); } - 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 document_reference::count_elements() & noexcept { return doc->count_elements(); } - simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } - simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } - simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } - simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } - simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } - simdjson_inline simdjson_result document_reference::find_field(const char* key) & noexcept { return doc->find_field(key); } - simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } - simdjson_inline simdjson_result document_reference::operator[](const char* key) & noexcept { return (*doc)[key]; } - simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } - simdjson_inline simdjson_result document_reference::find_field_unordered(const char* key) & noexcept { return doc->find_field_unordered(key); } - simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } - simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } - simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } - simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } - simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } - simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } - simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } - simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } - simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } - simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } - simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } - simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } - simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json(); } - simdjson_inline document_reference::operator document& () const noexcept { return *doc; } - - } // namespace ondemand - } // namespace lasx -} // namespace simdjson - - - -namespace simdjson { - simdjson_inline simdjson_result::simdjson_result(lasx::ondemand::document_reference value, error_code error) - noexcept : implementation_simdjson_result_base(std::forward(value), error) {} - - - simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); - } - simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); - } - simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; - } - simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() & noexcept { - return {}; - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char* key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](const char* key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::find_field(const char* key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { - if (error()) { return error(); } - return first.get_array(); - } - simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { - if (error()) { return error(); } - return first.get_object(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); - } - simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); - } - simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); - } - template - simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(receiver, allow_replacement); - } - simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); - } - simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); - } - simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); - } - simdjson_inline simdjson_result simdjson_result::get_value() noexcept { - if (error()) { return error(); } - return first.get_value(); - } - simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); - } - simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); - } - simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); - } - simdjson_inline simdjson_result simdjson_result::is_string() noexcept { - if (error()) { return error(); } - return first.is_string(); - } - simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); - } - simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); - } - simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); - } - simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); - } -#if SIMDJSON_EXCEPTIONS - template ::value == false>::type> - simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator lasx::ondemand::array() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator lasx::ondemand::object() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator lasx::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator lasx::ondemand::value() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } -#endif - - simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); - } - - simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); - } - - simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } - - simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { - return error(); + } 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 first.at_path(json_path); + } } + }; + return SUCCESS; +} + +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +} // namespace ondemand +} // namespace lasx +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + lasx::ondemand::document &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base( + error + ) +{ +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { + if (error()) { return error(); } + return first.get_value(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} + +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} + +template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; +template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first); +} +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(lasx::ondemand::document &out) & noexcept = delete; +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(lasx::ondemand::document &out) && noexcept { + if (error()) { return error(); } + out = std::forward(first); + return SUCCESS; +} + +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} + +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} + +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} + +simdjson_inline bool simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} + +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} + +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} + +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} + + +#if SIMDJSON_EXCEPTIONS +template ::value == false>::type> +simdjson_inline simdjson_result::operator T() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator lasx::ondemand::array() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator lasx::ondemand::object() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator lasx::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator lasx::ondemand::value() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline bool simdjson_result::at_end() const noexcept { + if (error()) { return error(); } + return first.at_end(); +} + + +simdjson_inline int32_t simdjson_result::current_depth() const noexcept { + if (error()) { return error(); } + return first.current_depth(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path(json_path); +} + +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path_with_wildcard(json_path); +} + +#if SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(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 document_reference::get_array() & noexcept { return doc->get_array(); } +simdjson_inline simdjson_result 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 document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } +simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } +simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } +template +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 document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } +simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } +simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } +simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } +simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline document_reference::operator T() noexcept(false) { return get(); } +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 document_reference::count_elements() & noexcept { return doc->count_elements(); } +simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } +simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } +simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } +simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } +simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } +simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } +simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } +simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } +simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } +simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } +simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } +simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } +simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } +simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } +simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } +simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } +simdjson_inline simdjson_result> document_reference::at_path_with_wildcard(std::string_view json_path) noexcept { return doc->at_path_with_wildcard(json_path); } +simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} +simdjson_inline document_reference::operator document&() const noexcept { return *doc; } +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code document_reference::extract_into(T& out) & noexcept { + return doc->extract_into(out); +} +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION +} // namespace ondemand +} // namespace lasx +} // namespace simdjson + + + +namespace simdjson { +simdjson_inline simdjson_result::simdjson_result(lasx::ondemand::document_reference value, error_code error) + noexcept : implementation_simdjson_result_base(std::forward(value), error) {} + + +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { + if (error()) { return error(); } + return first.get_value(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} +template <> +simdjson_warn_unused simdjson_inline error_code simdjson_result::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::get(lasx::ondemand::document_reference &out) && noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline simdjson_result::operator T() noexcept(false) { + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + if (error()) { throw simdjson_error(error()); } + return first.get(); +} +simdjson_inline simdjson_result::operator lasx::ondemand::array() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator lasx::ondemand::object() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator lasx::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator lasx::ondemand::value() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path_with_wildcard(json_path); +} +#if SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(out); +} +#endif // SIMDJSON_STATIC_REFLECTION } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H @@ -114628,423 +163978,432 @@ namespace simdjson { #include namespace simdjson { - namespace lasx { - namespace ondemand { +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 lock(locking_mutex); - cond_var.wait(lock, [this] {return has_work == false; }); - } +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 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 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 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 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::start_thread() { + std::unique_lock 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 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 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::stop_thread() { + std::unique_lock 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 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(); - } +inline void stage1_worker::run(document_stream * ds, parser * stage1, size_t next_batch_start) { + std::unique_lock 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 - ) 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 }, - error{ SUCCESS } +simdjson_inline document_stream::document_stream( + ondemand::parser &_parser, + const uint8_t *_buf, + size_t _len, + size_t _batch_size, + bool _allow_comma_separated +) 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}, + 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 - , use_thread(_parser.threaded) // we need to make a copy because _parser.threaded can change + if(worker.get() == nullptr) { + error = MEMALLOC; + } #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 }, - allow_comma_separated{ false }, - error{ UNINITIALIZED } -#ifdef SIMDJSON_THREADS_ENABLED - , use_thread(false) -#endif - { - } +simdjson_inline document_stream::document_stream() noexcept + : parser{nullptr}, + buf{nullptr}, + len{0}, + batch_size{0}, + allow_comma_separated{false}, + 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::~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::size_in_bytes() const noexcept { + return len; +} - inline size_t document_stream::truncated_bytes() const noexcept { - 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::truncated_bytes() const noexcept { + 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() 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 document_stream::iterator::iterator(document_stream* _stream, bool is_end) noexcept + : stream{_stream}, finished{is_end} { +} - simdjson_inline simdjson_result document_stream::iterator::operator*() noexcept { - //if(stream->error) { return stream->error; } - return simdjson_result(stream->doc, stream->error); - } +simdjson_inline simdjson_result document_stream::iterator::operator*() noexcept { + return simdjson_result(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 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; - } +simdjson_inline bool document_stream::iterator::at_end() const noexcept { + return 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); - } +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->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; } - doc_index = batch_start; - doc = document(json_iterator(&buf[batch_start], parser)); - doc.iter._streaming = true; +simdjson_inline bool document_stream::iterator::operator==(const document_stream::iterator &other) const noexcept { + return finished == other.finished; +} -#ifdef SIMDJSON_THREADS_ENABLED - if (use_thread && next_batch_start() < len) { - // Kick off the first thread on next batch if needed - error = stage1_thread_parser.allocate(batch_size); - if (error) { return; } - worker->start_thread(); - start_stage1_thread(); - if (error) { return; } - } -#endif // SIMDJSON_THREADS_ENABLED - } +simdjson_inline document_stream::iterator document_stream::begin() noexcept { + start(); + // If there are no documents, we're finished. + return iterator(this, error == EMPTY); +} - 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]; +simdjson_inline document_stream::iterator document_stream::end() noexcept { + return iterator(this, true); +} - // Check if at end of structural indexes (i.e. at end of batch) - if (cur_struct_index >= static_cast(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. - */ +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; } + doc_index = batch_start; + doc = document(json_iterator(&buf[batch_start], parser)); + doc.iter._streaming = true; - if (error) { continue; } // If the error was EMPTY, we may want to load another batch. - doc_index = batch_start; - } - } - } + #ifdef SIMDJSON_THREADS_ENABLED + if (use_thread && next_batch_start() < len) { + // Kick off the first thread on next batch if needed + 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_document() noexcept { - // 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) { doc.iter.consume_character(','); } - // 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 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]; - inline size_t document_stream::next_batch_start() const noexcept { - return batch_start + parser->implementation->structural_indexes[parser->implementation->n_structural_indexes]; - } + // Check if at end of structural indexes (i.e. at end of batch) + if(cur_struct_index >= static_cast(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. + */ - 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; - if (remaining <= batch_size) { - return p.implementation->stage1(&buf[_batch_start], remaining, stage1_mode::streaming_final); - } - else { - return p.implementation->stage1(&buf[_batch_start], batch_size, stage1_mode::streaming_partial); - } - } + if (error) { continue; } // If the error was EMPTY, we may want to load another batch. + doc_index = batch_start; + } + } +} - simdjson_inline size_t document_stream::iterator::current_index() const noexcept { - return stream->doc_index; - } +inline void document_stream::next_document() noexcept { + // 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(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(); +} - simdjson_inline std::string_view document_stream::iterator::source() const noexcept { - auto depth = stream->doc.iter.depth(); - auto cur_struct_index = stream->doc.iter._root - stream->parser->implementation->structural_indexes.get(); +inline size_t document_stream::next_batch_start() const noexcept { + return batch_start + parser->implementation->structural_indexes[parser->implementation->n_structural_indexes]; +} - // If at root, process the first token to determine if scalar value - if (stream->doc.iter.at_root()) { - 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 - // TODO: We could remove trailing whitespaces - // This returns a string spanning from start of value to the beginning of the next document (excluded) - { - auto next_index = 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(stream->buf) + current_index(); - while (svlen > 1 && (std::isspace(start[svlen - 1]) || start[svlen - 1] == '\0')) { - svlen--; - } - return std::string_view(start, svlen); - } - } - cur_struct_index++; - } +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; + if (remaining <= batch_size) { + return p.implementation->stage1(&buf[_batch_start], remaining, stage1_mode::streaming_final); + } else { + return p.implementation->stage1(&buf[_batch_start], batch_size, stage1_mode::streaming_partial); + } +} - while (cur_struct_index <= static_cast(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++; - } +simdjson_inline size_t document_stream::iterator::current_index() const noexcept { + return stream->doc_index; +} - return std::string_view(reinterpret_cast(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[cur_struct_index] - current_index() + stream->batch_start + 1);; - } +simdjson_inline std::string_view document_stream::iterator::source() const noexcept { + auto depth = stream->doc.iter.depth(); + auto cur_struct_index = stream->doc.iter._root - stream->parser->implementation->structural_indexes.get(); - inline error_code document_stream::iterator::error() const noexcept { - return stream->error; - } + // If at root, process the first token to determine if scalar value + if (stream->doc.iter.at_root()) { + 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 + // TODO: We could remove trailing whitespaces + // This returns a string spanning from start of value to the beginning of the next document (excluded) + { + auto next_index = 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(stream->buf) + current_index(); + while(svlen > 1 && (std::isspace(start[svlen-1]) || start[svlen-1] == '\0')) { + svlen--; + } + return std::string_view(start, svlen); + } + } + cur_struct_index++; + } + + while (cur_struct_index <= static_cast(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(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; } +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(); - } - } + // 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(); +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); - } + worker->run(this, & this->stage1_thread_parser, _next_batch_start); +} #endif // SIMDJSON_THREADS_ENABLED - } // namespace ondemand - } // namespace lasx +} // namespace ondemand +} // namespace lasx } // namespace simdjson namespace simdjson { - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept : - implementation_simdjson_result_base(error) - { - } - simdjson_inline simdjson_result::simdjson_result( - lasx::ondemand::document_stream&& value - ) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) - { - } +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base(error) +{ +} +simdjson_inline simdjson_result::simdjson_result( + lasx::ondemand::document_stream &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} } @@ -115063,120 +164422,120 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace ondemand { +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() {} +// clang 6 does not think the default constructor can be noexcept, so we make it explicit +simdjson_inline field::field() noexcept : std::pair() {} - simdjson_inline field::field(raw_json_string key, ondemand::value&& value) noexcept - : std::pair(key, std::forward(value)) - { - } +simdjson_inline field::field(raw_json_string key, ondemand::value &&value) noexcept + : std::pair(key, std::forward(value)) +{ +} - simdjson_inline simdjson_result 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::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::start(const value_iterator& parent_iter, raw_json_string key) noexcept { - return field(key, parent_iter.child()); - } +simdjson_inline simdjson_result 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 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 answer = first.unescape(second.iter.json_iter(), allow_replacement); - first.consume(); - return answer; - } +simdjson_inline simdjson_warn_unused simdjson_result 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 answer = first.unescape(second.iter.json_iter(), allow_replacement); + first.consume(); + return answer; +} - template - 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)); - receiver = key; - return SUCCESS; - } +template +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) ); + 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 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(first.buf - 1), second.iter._json_iter->token.peek(-1) - first.buf + 1); - } +simdjson_inline std::string_view field::key_raw_json_token() const noexcept { + SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us. + return std::string_view(reinterpret_cast(first.buf-1), second.iter._json_iter->token.peek(-1) - first.buf + 1); +} - simdjson_inline 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(first.buf), end_quote - first.buf); - } +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(first.buf), end_quote - first.buf); +} - simdjson_inline value& field::value() & noexcept { - return second; - } +simdjson_inline value &field::value() & noexcept { + return second; +} - simdjson_inline value field::value() && noexcept { - return std::forward(*this).second; - } +simdjson_inline value field::value() && noexcept { + return std::forward(*this).second; +} - } // namespace ondemand - } // namespace lasx +} // namespace ondemand +} // namespace lasx } // namespace simdjson namespace simdjson { - simdjson_inline simdjson_result::simdjson_result( - lasx::ondemand::field&& value - ) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) - { - } - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept : - implementation_simdjson_result_base(error) - { - } +simdjson_inline simdjson_result::simdjson_result( + lasx::ondemand::field &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base(error) +{ +} - simdjson_inline simdjson_result simdjson_result::key() noexcept { - if (error()) { return error(); } - return first.key(); - } +simdjson_inline simdjson_result simdjson_result::key() noexcept { + if (error()) { return error(); } + return first.key(); +} - simdjson_inline simdjson_result simdjson_result::key_raw_json_token() noexcept { - if (error()) { return error(); } - return first.key_raw_json_token(); - } +simdjson_inline simdjson_result simdjson_result::key_raw_json_token() noexcept { + if (error()) { return error(); } + return first.key_raw_json_token(); +} - simdjson_inline simdjson_result simdjson_result::escaped_key() noexcept { - if (error()) { return error(); } - return first.escaped_key(); - } +simdjson_inline simdjson_result simdjson_result::escaped_key() noexcept { + if (error()) { return error(); } + return first.escaped_key(); +} - simdjson_inline simdjson_result simdjson_result::unescaped_key(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.unescaped_key(allow_replacement); - } +simdjson_inline simdjson_result simdjson_result::unescaped_key(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.unescaped_key(allow_replacement); +} - template - simdjson_inline error_code simdjson_result::unescaped_key(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.unescaped_key(receiver, allow_replacement); - } +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::unescaped_key(string_type &receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.unescaped_key(receiver, allow_replacement); +} - simdjson_inline simdjson_result simdjson_result::value() noexcept { - if (error()) { return error(); } - return std::move(first.value()); - } +simdjson_inline simdjson_result simdjson_result::value() noexcept { + if (error()) { return error(); } + return std::move(first.value()); +} } // namespace simdjson @@ -115199,432 +164558,440 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace ondemand { +namespace lasx { +namespace ondemand { - simdjson_inline json_iterator::json_iterator(json_iterator&& other) noexcept - : token(std::forward(other.token)), - parser{ other.parser }, - _string_buf_loc{ other._string_buf_loc }, - error{ other.error }, - _depth{ other._depth }, - _root{ other._root }, - _streaming{ other._streaming } - { - 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; - other.parser = nullptr; - return *this; - } +simdjson_inline json_iterator::json_iterator(json_iterator &&other) noexcept + : token(std::forward(other.token)), + parser{other.parser}, + _string_buf_loc{other._string_buf_loc}, + error{other.error}, + _depth{other._depth}, + _root{other._root}, + _streaming{other._streaming} +{ + 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; + 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 } +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(); +{ + logger::log_headers(); #if SIMDJSON_CHECK_EOF - assert_more_tokens(); + 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 } +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} - { - logger::log_headers(); +{ + logger::log_headers(); #if SIMDJSON_CHECK_EOF - assert_more_tokens(); + 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 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; - } +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 +// 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; } + // 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"); } + // 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 '"': - if (*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; - } + break; + case '"': + if(*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; - } - } + // 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(TAPE_ERROR, "not enough close braces"); - } + return report_error(TAPE_ERROR, "not enough close braces"); +} - SIMDJSON_POP_DISABLE_WARNINGS +SIMDJSON_POP_DISABLE_WARNINGS - simdjson_inline bool json_iterator::at_root() const noexcept { - return position() == root_position(); - } +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::is_single_token() const noexcept { + return parser->implementation->n_structural_indexes == 1; +} - simdjson_inline bool json_iterator::streaming() const noexcept { - return _streaming; - } +simdjson_inline bool json_iterator::streaming() const noexcept { + return _streaming; +} - simdjson_inline token_position json_iterator::root_position() const noexcept { - return _root; - } +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_document_depth() const noexcept { + SIMDJSON_ASSUME( _depth == 1 ); +} - simdjson_inline void json_iterator::assert_at_root() 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); + // 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_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 { +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]); + 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]; - } +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(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 std::string json_iterator::to_string() const noexcept { + if( !is_alive() ) { return "dead json_iterator instance"; } + const char * current_structural = reinterpret_cast(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 json_iterator::current_location() const noexcept { - if (!is_alive()) { // Unrecoverable error - if (!at_root()) { - return reinterpret_cast(token.peek(-1)); - } - else { - return reinterpret_cast(token.peek()); - } - } - if (at_end()) { - return OUT_OF_BOUNDS; - } - return reinterpret_cast(token.peek()); - } +inline simdjson_result json_iterator::current_location() const noexcept { + if (!is_alive()) { // Unrecoverable error + if (!at_root()) { + return reinterpret_cast(token.peek(-1)); + } else { + return reinterpret_cast(token.peek()); + } + } + if (at_end()) { + return OUT_OF_BOUNDS; + } + return reinterpret_cast(token.peek()); +} - simdjson_inline bool json_iterator::is_alive() const noexcept { - return parser; - } +simdjson_inline bool json_iterator::is_alive() const noexcept { + return parser; +} - simdjson_inline void json_iterator::abandon() noexcept { - parser = nullptr; - _depth = 0; - } +simdjson_inline void json_iterator::abandon() noexcept { + parser = nullptr; + _depth = 0; +} - simdjson_inline const uint8_t* json_iterator::return_current_and_advance() noexcept { +simdjson_inline const uint8_t *json_iterator::return_current_and_advance() noexcept { #if SIMDJSON_CHECK_EOF - assert_more_tokens(); + assert_more_tokens(); #endif // SIMDJSON_CHECK_EOF - return token.return_current_and_advance(); - } + 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::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 { +simdjson_inline const uint8_t *json_iterator::peek(int32_t delta) const noexcept { #if SIMDJSON_CHECK_EOF - assert_more_tokens(delta + 1); + assert_more_tokens(delta+1); #endif // SIMDJSON_CHECK_EOF - return token.peek(delta); - } + return token.peek(delta); +} - simdjson_inline uint32_t json_iterator::peek_length(int32_t delta) const noexcept { +simdjson_inline uint32_t json_iterator::peek_length(int32_t delta) const noexcept { #if SIMDJSON_CHECK_EOF - assert_more_tokens(delta + 1); + assert_more_tokens(delta+1); #endif // #if SIMDJSON_CHECK_EOF - return token.peek_length(delta); - } + 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 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 { +simdjson_inline uint32_t json_iterator::peek_length(token_position position) const noexcept { #if SIMDJSON_CHECK_EOF - assert_valid_position(position); + 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 { + 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); + assert_valid_position(position); #endif // SIMDJSON_CHECK_EOF - return token.peek_root_length(position); - } + 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 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::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 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 depth_t json_iterator::depth() const noexcept { + return _depth; +} - simdjson_inline uint8_t*& json_iterator::string_buf_loc() noexcept { - return _string_buf_loc; - } +simdjson_inline uint8_t *&json_iterator::string_buf_loc() noexcept { + return _string_buf_loc; +} - 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_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 token_position json_iterator::position() const noexcept { + return token.position(); +} - simdjson_inline simdjson_result json_iterator::unescape(raw_json_string in, bool allow_replacement) noexcept { +simdjson_inline simdjson_result 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); - SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc)); - return result; + 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); + return parser->unescape(in, _string_buf_loc, allow_replacement); #endif - } +} - simdjson_inline simdjson_result json_iterator::unescape_wobbly(raw_json_string in) noexcept { +simdjson_inline simdjson_result json_iterator::unescape_wobbly(raw_json_string in) noexcept { #if SIMDJSON_DEVELOPMENT_CHECKS - auto result = parser->unescape_wobbly(in, _string_buf_loc); - SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc)); - return result; + 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); + 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); +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]); + 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; - } + token.set_position(position); + _depth = child_depth; +} - simdjson_inline error_code json_iterator::consume_character(char c) noexcept { - if (*peek() == c) { - return_current_and_advance(); - return SUCCESS; - } - return TAPE_ERROR; - } +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 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; } - } +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_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 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; - } +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 ondemand +} // namespace lasx } // namespace simdjson namespace simdjson { - simdjson_inline simdjson_result::simdjson_result(lasx::ondemand::json_iterator&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} +simdjson_inline simdjson_result::simdjson_result(lasx::ondemand::json_iterator &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(error) {} } // namespace simdjson @@ -115642,109 +165009,109 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace ondemand { +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; - default: SIMDJSON_UNREACHABLE(); - } - return out; - } +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; + default: SIMDJSON_UNREACHABLE(); + } + return out; +} #if SIMDJSON_EXCEPTIONS - inline std::ostream& operator<<(std::ostream& out, simdjson_result& type) noexcept(false) { - return out << type.value(); - } +inline std::ostream& operator<<(std::ostream& out, simdjson_result &type) noexcept(false) { + return out << type.value(); +} #endif - simdjson_inline number_type number::get_number_type() const noexcept { - return type; - } +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 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 uint64_t number::get_uint64() const noexcept { + return payload.unsigned_integer; +} - simdjson_inline number::operator uint64_t() const noexcept { - return get_uint64(); - } +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 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 int64_t number::get_int64() const noexcept { + return payload.signed_integer; +} - simdjson_inline number::operator int64_t() const noexcept { - return get_int64(); - } +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 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 double number::get_double() const noexcept { + return payload.floating_point_number; +} - simdjson_inline number::operator double() const noexcept { - return get_double(); - } +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 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_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_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::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; - } +simdjson_inline void number::skip_double() noexcept { + type = number_type::floating_point_number; +} - } // namespace ondemand - } // namespace lasx +} // namespace ondemand +} // namespace lasx } // namespace simdjson namespace simdjson { - simdjson_inline simdjson_result::simdjson_result(lasx::ondemand::json_type&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} +simdjson_inline simdjson_result::simdjson_result(lasx::ondemand::json_type &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(error) {} } // namespace simdjson @@ -115766,216 +165133,214 @@ namespace simdjson { #include namespace simdjson { - namespace lasx { - namespace ondemand { - namespace logger { +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. +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 ' '; - } - } +// Helper to turn unprintable or newline characters into spaces +static inline char printable_char(char c) { + if (c >= 0x20) { + return c; + } else { + return ' '; + } +} - template - 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_s); - if (size <= 0) return std::string(); - std::unique_ptr 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); - } +template +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_s); + if (size <= 0) return std::string(); + std::unique_ptr 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 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 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(); - } +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_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_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_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_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_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_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_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_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_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_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"); +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); - } - } - } + 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 - 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)...); - } +template +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)...); +} - template - 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)...); - 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); - } - } - } +template +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)...); + 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 logger +} // namespace ondemand +} // namespace lasx } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_LOGGER_INL_H @@ -115993,269 +165358,369 @@ namespace simdjson { /* 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): #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 */ +/* amalgamation skipped (editor-only): #endif */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace ondemand { +namespace lasx { +namespace ondemand { - simdjson_inline simdjson_result 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(key.size()), key.data()); - return NO_SUCH_FIELD; - } - return value(iter.child()); - } - simdjson_inline simdjson_result 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(key.size()), key.data()); - return NO_SUCH_FIELD; - } - return value(iter.child()); - } - simdjson_inline simdjson_result object::operator[](const std::string_view key) & noexcept { - return find_field_unordered(key); - } - simdjson_inline simdjson_result object::operator[](const std::string_view key) && noexcept { - return std::forward(*this).find_field_unordered(key); - } - simdjson_inline simdjson_result object::find_field(const std::string_view key) & noexcept { - 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(key.size()), key.data()); - return NO_SUCH_FIELD; - } - return value(iter.child()); - } - simdjson_inline simdjson_result object::find_field(const std::string_view key) && noexcept { - 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(key.size()), key.data()); - return NO_SUCH_FIELD; - } - return value(iter.child()); - } +simdjson_inline simdjson_result 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} +simdjson_inline simdjson_result 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} +simdjson_inline simdjson_result object::operator[](const std::string_view key) & noexcept { + return find_field_unordered(key); +} +simdjson_inline simdjson_result object::operator[](const std::string_view key) && noexcept { + return std::forward(*this).find_field_unordered(key); +} +simdjson_inline simdjson_result object::find_field(const std::string_view key) & noexcept { + 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} +simdjson_inline simdjson_result object::find_field(const std::string_view key) && noexcept { + 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} - simdjson_inline simdjson_result object::start(value_iterator& iter) noexcept { - SIMDJSON_TRY(iter.start_object().error()); - return object(iter); - } - simdjson_inline simdjson_result object::start_root(value_iterator& iter) noexcept { - SIMDJSON_TRY(iter.start_root_object().error()); - return object(iter); - } - 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 object::start(value_iterator &iter) noexcept { + SIMDJSON_TRY( iter.start_object().error() ); + return object(iter); +} +simdjson_inline simdjson_result 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 object::raw_json() noexcept { - 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(starting_point), size_t(final_point - starting_point)); - } +simdjson_inline simdjson_result object::raw_json() noexcept { + 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(starting_point), size_t(final_point - starting_point)); +} - simdjson_inline simdjson_result object::started(value_iterator& iter) noexcept { - SIMDJSON_TRY(iter.started_object().error()); - return object(iter); - } +simdjson_inline simdjson_result 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::resume(const value_iterator &iter) noexcept { + return iter; +} - simdjson_inline object::object(const value_iterator& _iter) noexcept - : iter{ _iter } - { - } +simdjson_inline object::object(const value_iterator &_iter) noexcept + : iter{_iter} +{ +} - simdjson_inline simdjson_result object::begin() noexcept { +simdjson_inline simdjson_result object::begin() noexcept { #if SIMDJSON_DEVELOPMENT_CHECKS - if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } + if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } #endif - return object_iterator(iter); - } - simdjson_inline simdjson_result object::end() noexcept { - return object_iterator(iter); - } + return object_iterator(iter); +} +simdjson_inline simdjson_result object::end() noexcept { + return object_iterator(iter); +} - inline simdjson_result object::at_pointer(std::string_view json_pointer) noexcept { - if (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 child; +inline simdjson_result object::at_pointer(std::string_view json_pointer) noexcept { + if (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 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; + // 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 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); +} + +inline simdjson_result> object::at_path_with_wildcard(std::string_view json_path) noexcept { + std::vector result; + + 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; + // 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()) { + result.push_back(std::move(val)); + } else { + auto nested_result = val.at_path_with_wildcard(remaining_path); + + if (nested_result.error()) { + return nested_result.error(); + } + // Extract and append all nested matches to our result + std::vector nested_vec; + SIMDJSON_TRY(std::move(nested_result).get(nested_vec)); + + result.insert(result.end(), + std::make_move_iterator(nested_vec.begin()), + std::make_move_iterator(nested_vec.end())); + } + } + return result; + } else { + value val; + SIMDJSON_TRY(find_field(key).get(val)); + + if (remaining_path.empty()) { + result.push_back(std::move(val)); + return result; + } else { + return val.at_path_with_wildcard(remaining_path); + } + } +} + +simdjson_inline simdjson_result 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 object::is_empty() & noexcept { + 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 object::reset() & noexcept { + return iter.reset_object(); +} + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (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) { + // 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:])); + } + } + } + }; - inline simdjson_result object::at_path(std::string_view json_path) noexcept { - auto json_pointer = json_path_to_pointer_conversion(json_path); - if (json_pointer == "-1") { - return INVALID_JSON_POINTER; - } - return at_pointer(json_pointer); - } + return SUCCESS; +} - simdjson_inline simdjson_result 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; - } +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION - simdjson_inline simdjson_result object::is_empty() & noexcept { - 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 object::reset() & noexcept { - return iter.reset_object(); - } - - } // namespace ondemand - } // namespace lasx +} // namespace ondemand +} // namespace lasx } // namespace simdjson namespace simdjson { - simdjson_inline simdjson_result::simdjson_result(lasx::ondemand::object&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} +simdjson_inline simdjson_result::simdjson_result(lasx::ondemand::object &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(error) {} - simdjson_inline simdjson_result simdjson_result::begin() noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() noexcept { - if (error()) { return error(); } - return first.end(); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) && noexcept { - if (error()) { return error(); } - return std::forward(first).find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) && noexcept { - if (error()) { return error(); } - return std::forward(first)[key]; - } - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) && noexcept { - if (error()) { return error(); } - return std::forward(first).find_field(key); - } +simdjson_inline simdjson_result simdjson_result::begin() noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() noexcept { + if (error()) { return error(); } + return first.end(); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) && noexcept { + if (error()) { return error(); } + return std::forward(first).find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) && noexcept { + if (error()) { return error(); } + return std::forward(first)[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) && noexcept { + if (error()) { return error(); } + return std::forward(first).find_field(key); +} - simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); - } +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} - simdjson_inline simdjson_result simdjson_result::at_path( - std::string_view json_path) noexcept { - if (error()) { - return error(); - } - return first.at_path(json_path); - } +simdjson_inline simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} - inline simdjson_result simdjson_result::reset() noexcept { - if (error()) { return error(); } - return first.reset(); - } +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path_with_wildcard(json_path); +} - inline simdjson_result simdjson_result::is_empty() noexcept { - if (error()) { return error(); } - return first.is_empty(); - } +inline simdjson_result simdjson_result::reset() noexcept { + if (error()) { return error(); } + return first.reset(); +} - simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); - } +inline simdjson_result simdjson_result::is_empty() noexcept { + if (error()) { return error(); } + return first.is_empty(); +} + +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { + if (error()) { return error(); } + return first.raw_json(); +} - simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { - if (error()) { return error(); } - return first.raw_json(); - } } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H @@ -116273,129 +165738,139 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace ondemand { +namespace lasx { +namespace ondemand { - // - // object_iterator - // +// +// object_iterator +// - simdjson_inline object_iterator::object_iterator(const value_iterator& _iter) noexcept - : iter{ _iter } - {} +simdjson_inline object_iterator::object_iterator(const value_iterator &_iter) noexcept + : iter{_iter} +{} - simdjson_inline simdjson_result object_iterator::operator*() noexcept { - 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_inline simdjson_result 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 { - // 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_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 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 + 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. - // +// +// ### 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 ondemand +} // namespace lasx } // namespace simdjson namespace simdjson { - simdjson_inline simdjson_result::simdjson_result( - lasx::ondemand::object_iterator&& value - ) noexcept - : implementation_simdjson_result_base(std::forward(value)) - { - first.iter.assert_is_valid(); - } - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base({}, error) - { - } +simdjson_inline simdjson_result::simdjson_result( + lasx::ondemand::object_iterator &&value +) noexcept + : implementation_simdjson_result_base(std::forward(value)) +{ + first.iter.assert_is_valid(); +} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base({}, error) +{ +} - simdjson_inline simdjson_result simdjson_result::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::operator==(const simdjson_result& 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::operator!=(const simdjson_result& other) const noexcept { - if (!first.iter.is_valid()) { return error(); } - return first != other.first; - } - // Checks for ']' and ',' - simdjson_inline simdjson_result& simdjson_result::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 simdjson_result simdjson_result::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::operator==(const simdjson_result &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::operator!=(const simdjson_result &other) const noexcept { + if (!first.iter.is_valid()) { return error(); } + return first != other.first; +} +// Checks for ']' and ',' +simdjson_inline simdjson_result &simdjson_result::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 @@ -116419,193 +165894,225 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace ondemand { +namespace lasx { +namespace ondemand { - simdjson_inline parser::parser(size_t max_capacity) noexcept - : _max_capacity{ max_capacity } { - } +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; } +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; - size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * new_capacity / 3 + SIMDJSON_PADDING, 64); - string_buf.reset(new (std::nothrow) uint8_t[string_capacity]); + // string_capacity copied from document::allocate + _capacity = 0; + 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]); + 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 (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()); - } +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 parser::iterate(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(padded_string_view json) & noexcept { + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } - json.remove_utf8_bom(); + json.remove_utf8_bom(); - // Allocate if needed - if (capacity() < json.length() || !string_buf) { - SIMDJSON_TRY(allocate(json.length(), max_depth())); - } + // 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(json.data()), json.length(), stage1_mode::regular)); - return document::start({ reinterpret_cast(json.data()), this }); - } + // Run stage 1. + SIMDJSON_TRY( implementation->stage1(reinterpret_cast(json.data()), json.length(), stage1_mode::regular) ); + return document::start({ reinterpret_cast(json.data()), this }); +} #ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON - simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept { + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } - json.remove_utf8_bom(); + json.remove_utf8_bom(); - // Allocate if needed - if (capacity() < json.length() || !string_buf) { - SIMDJSON_TRY(allocate(json.length(), max_depth())); - } + // 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(json.data()), json.length(), stage1_mode::regular); - if (err) { - if (err != UNCLOSED_STRING) - return err; - } - return document::start({ reinterpret_cast(json.data()), this, true }); - } + // Run stage 1. + const simdjson::error_code err = implementation->stage1(reinterpret_cast(json.data()), json.length(), stage1_mode::regular); + if (err) { + if (err != UNCLOSED_STRING) + return err; + } + return document::start({ reinterpret_cast(json.data()), this, true }); +} #endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON - simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 parser::iterate(std::string_view json, size_t allocated) & noexcept { - return iterate(padded_string_view(json, allocated)); - } +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(std::string_view json, size_t allocated) & noexcept { + return iterate(padded_string_view(json, allocated)); +} - simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(std::string& json) & noexcept { - if (json.capacity() - json.size() < SIMDJSON_PADDING) { - json.reserve(json.size() + SIMDJSON_PADDING); - } - return iterate(padded_string_view(json)); - } +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(std::string &json) & noexcept { + return iterate(pad_with_reserve(json)); +} - simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const std::string& json) & noexcept { - return iterate(padded_string_view(json)); - } +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const std::string &json) & noexcept { + return iterate(padded_string_view(json)); +} - simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const simdjson_result& result) & noexcept { - // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 parser::iterate(const simdjson_result &result) & noexcept { + // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 parser::iterate(const simdjson_result& result) & noexcept { - // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 parser::iterate(const simdjson_result &result) & noexcept { + // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 parser::iterate_raw(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_raw(padded_string_view json) & noexcept { + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } - json.remove_utf8_bom(); + json.remove_utf8_bom(); - // Allocate if needed - if (capacity() < json.length()) { - SIMDJSON_TRY(allocate(json.length(), max_depth())); - } + // Allocate if needed + if (capacity() < json.length()) { + SIMDJSON_TRY( allocate(json.length(), max_depth()) ); + } - // Run stage 1. - SIMDJSON_TRY(implementation->stage1(reinterpret_cast(json.data()), json.length(), stage1_mode::regular)); - return json_iterator(reinterpret_cast(json.data()), this); - } + // Run stage 1. + SIMDJSON_TRY( implementation->stage1(reinterpret_cast(json.data()), json.length(), stage1_mode::regular) ); + return json_iterator(reinterpret_cast(json.data()), this); +} - inline simdjson_result parser::iterate_many(const uint8_t* buf, size_t len, size_t batch_size, bool allow_comma_separated) 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 (allow_comma_separated && batch_size < len) { batch_size = len; } - return document_stream(*this, buf, len, batch_size, allow_comma_separated); - } - inline simdjson_result parser::iterate_many(const char* buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept { - return iterate_many(reinterpret_cast(buf), len, batch_size, allow_comma_separated); - } - inline simdjson_result parser::iterate_many(const std::string& s, size_t batch_size, bool allow_comma_separated) noexcept { - return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated); - } - inline simdjson_result parser::iterate_many(const padded_string& s, size_t batch_size, bool allow_comma_separated) noexcept { - return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated); - } +inline simdjson_result 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; + } + if(allow_comma_separated && batch_size < len) { batch_size = len; } + return document_stream(*this, buf, len, batch_size, allow_comma_separated); +} - 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; - } +inline simdjson_result 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(buf), len, batch_size, allow_comma_separated); +} +inline simdjson_result 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 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 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 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_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 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 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(dst), end - dst); - dst = end; - return result; - } +simdjson_inline simdjson_warn_unused simdjson_result 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(dst), end-dst); + dst = end; + return result; +} - simdjson_inline simdjson_warn_unused simdjson_result 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(dst), end - dst); - dst = end; - return result; - } +simdjson_inline simdjson_warn_unused simdjson_result 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(dst), end-dst); + dst = end; + return result; +} - } // namespace ondemand - } // namespace lasx +simdjson_inline simdjson_warn_unused ondemand::parser& parser::get_parser() { + return *parser::get_parser_instance(); +} + +simdjson_inline bool 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& parser::get_parser_instance() { + std::unique_ptr& 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& 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 parser_instance = nullptr; + return parser_instance; +} + + +} // namespace ondemand +} // namespace lasx } // namespace simdjson namespace simdjson { - simdjson_inline simdjson_result::simdjson_result(lasx::ondemand::parser&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} +simdjson_inline simdjson_result::simdjson_result(lasx::ondemand::parser &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(error) {} } // namespace simdjson @@ -116625,456 +166132,210 @@ namespace simdjson { namespace simdjson { - namespace lasx { - namespace ondemand { +namespace lasx { +namespace ondemand { - simdjson_inline raw_json_string::raw_json_string(const uint8_t* _buf) noexcept : buf{ _buf } {} +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(buf); } +simdjson_inline const char * raw_json_string::raw() const noexcept { + return reinterpret_cast(buf); +} + +simdjson_inline char raw_json_string::operator[](size_t i) const noexcept { + return reinterpret_cast(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::is_free_from_unescaped_quote(std::string_view target) noexcept { - size_t pos{ 0 }; - // if the content has no escape character, just scan through it quickly! - for (; pos < target.size() && target[pos] != '\\'; pos++) {} - // slow path may begin. - bool escaping{ false }; - for (; pos < target.size(); pos++) { - if ((target[pos] == '"') && !escaping) { - return false; - } - else if (target[pos] == '\\') { - escaping = !escaping; - } - else { - escaping = false; - } - } - 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::is_free_from_unescaped_quote(const char* target) noexcept { - size_t pos{ 0 }; - // if the content has no escape character, just scan through it quickly! - for (; target[pos] && target[pos] != '\\'; pos++) {} - // slow path may begin. - bool escaping{ false }; - for (; target[pos]; pos++) { - if ((target[pos] == '"') && !escaping) { - return false; - } - else if (target[pos] == '\\') { - escaping = !escaping; - } - else { - escaping = false; - } - } - return true; - } +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()); + } + const char * r{raw()}; + size_t pos{0}; + for(;pos < target.size();pos++) { + if(r[pos] != target[pos]) { return false; } + } + if(r[pos] != '"') { return false; } + return true; +} + +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(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(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::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()); - } - const char* r{ raw() }; - size_t pos{ 0 }; - for (; pos < target.size(); 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_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_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 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 simdjson_warn_unused simdjson_result raw_json_string::unescape(json_iterator &iter, bool allow_replacement) const noexcept { + return iter.unescape(*this, allow_replacement); +} - 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_inline simdjson_warn_unused simdjson_result raw_json_string::unescape_wobbly(json_iterator &iter) const noexcept { + return iter.unescape_wobbly(*this); +} - 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 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++; + } +} - 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 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 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 ondemand +} // namespace lasx } // namespace simdjson namespace simdjson { - simdjson_inline simdjson_result::simdjson_result(lasx::ondemand::raw_json_string&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} +simdjson_inline simdjson_result::simdjson_result(lasx::ondemand::raw_json_string &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(error) {} - simdjson_inline simdjson_result simdjson_result::raw() const noexcept { - if (error()) { return error(); } - return first.raw(); - } - simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::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 simdjson_result::unescape_wobbly(lasx::ondemand::json_iterator& iter) const noexcept { - if (error()) { return error(); } - return first.unescape_wobbly(iter); - } +simdjson_inline simdjson_result simdjson_result::raw() const noexcept { + if (error()) { return error(); } + return first.raw(); +} +simdjson_inline char simdjson_result::operator[](size_t i) const noexcept { + if (error()) { return error(); } + return first[i]; +} +simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::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 simdjson_result::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/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): #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 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 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 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 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 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 to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); - } - - inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); - } - - inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); - } - - inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); - } - - inline simdjson_result to_json_string(simdjson_result 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 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 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&& x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); - } - inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& 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 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 */ /* 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 @@ -117087,637 +166348,91 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace ondemand { +namespace lasx { +namespace ondemand { - simdjson_inline token_iterator::token_iterator( - const uint8_t* _buf, - token_position position - ) noexcept : buf{ _buf }, _position{ position } - { - } +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 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::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 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 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 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; - } +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 ondemand +} // namespace lasx } // namespace simdjson namespace simdjson { - simdjson_inline simdjson_result::simdjson_result(lasx::ondemand::token_iterator&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} +simdjson_inline simdjson_result::simdjson_result(lasx::ondemand::token_iterator &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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-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 */ - -namespace simdjson { - namespace lasx { - namespace ondemand { - - 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 value::get_array() noexcept { - return array::start(iter); - } - simdjson_inline simdjson_result value::get_object() noexcept { - return object::start(iter); - } - simdjson_inline simdjson_result value::start_or_resume_object() noexcept { - if (iter.at_start()) { - return get_object(); - } - else { - return object::resume(iter); - } - } - - simdjson_inline simdjson_result value::get_raw_json_string() noexcept { - return iter.get_raw_json_string(); - } - simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { - return iter.get_string(allow_replacement); - } - template - 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 value::get_wobbly_string() noexcept { - return iter.get_wobbly_string(); - } - simdjson_inline simdjson_result value::get_double() noexcept { - return iter.get_double(); - } - simdjson_inline simdjson_result value::get_double_in_string() noexcept { - return iter.get_double_in_string(); - } - simdjson_inline simdjson_result value::get_uint64() noexcept { - return iter.get_uint64(); - } - simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { - return iter.get_uint64_in_string(); - } - simdjson_inline simdjson_result value::get_int64() noexcept { - return iter.get_int64(); - } - simdjson_inline simdjson_result value::get_int64_in_string() noexcept { - return iter.get_int64_in_string(); - } - simdjson_inline simdjson_result value::get_bool() noexcept { - return iter.get_bool(); - } - simdjson_inline simdjson_result value::is_null() noexcept { - return iter.is_null(); - } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } - template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } - - template simdjson_inline error_code value::get(T& out) noexcept { - return get().get(out); - } - -#if SIMDJSON_EXCEPTIONS - template - simdjson_inline value::operator T() noexcept(false) { - return get(); - } - 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 value::begin() & noexcept { - return get_array().begin(); - } - simdjson_inline simdjson_result value::end() & noexcept { - return {}; - } - simdjson_inline simdjson_result value::count_elements() & noexcept { - simdjson_result 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 value::count_fields() & noexcept { - simdjson_result answer; - auto a = get_object(); - answer = a.count_fields(); - iter.move_at_start(); - return answer; - } - simdjson_inline simdjson_result value::at(size_t index) noexcept { - auto a = get_array(); - return a.at(index); - } - - simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { - return start_or_resume_object().find_field(key); - } - simdjson_inline simdjson_result value::find_field(const char* key) noexcept { - return start_or_resume_object().find_field(key); - } - - simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { - return start_or_resume_object().find_field_unordered(key); - } - simdjson_inline simdjson_result value::find_field_unordered(const char* key) noexcept { - return start_or_resume_object().find_field_unordered(key); - } - - simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { - return start_or_resume_object()[key]; - } - simdjson_inline simdjson_result value::operator[](const char* key) noexcept { - return start_or_resume_object()[key]; - } - - simdjson_inline simdjson_result value::type() noexcept { - return iter.type(); - } - - simdjson_inline simdjson_result 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 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 value::is_integer() noexcept { - return iter.is_integer(); - } - simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { - return iter.get_number_type(); - } - simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { - return iter.get_number(); - } - - simdjson_inline std::string_view value::raw_json_token() noexcept { - return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); - } - - simdjson_inline simdjson_result 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 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::at_pointer(std::string_view json_pointer) noexcept { - 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::at_path(std::string_view json_path) noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) { - case json_type::array: - return (*this).get_array().at_path(json_path); - case json_type::object: - return (*this).get_object().at_path(json_path); - default: - return INVALID_JSON_POINTER; - } - } - - } // namespace ondemand - } // namespace lasx -} // namespace simdjson - -namespace simdjson { - - simdjson_inline simdjson_result::simdjson_result( - lasx::ondemand::value&& value - ) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) - { - } - simdjson_inline simdjson_result::simdjson_result( - error_code error - ) noexcept : - implementation_simdjson_result_base(error) - { - } - simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); - } - simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); - } - simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { - if (error()) { return error(); } - return first.at(index); - } - simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); - } - simdjson_inline simdjson_result simdjson_result::end() & noexcept { - if (error()) { return error(); } - return {}; - } - - simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - simdjson_inline simdjson_result simdjson_result::find_field(const char* key) noexcept { - if (error()) { return error(); } - return first.find_field(key); - } - - simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char* key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); - } - - simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { - if (error()) { return error(); } - return first[key]; - } - simdjson_inline simdjson_result simdjson_result::operator[](const char* key) noexcept { - if (error()) { return error(); } - return first[key]; - } - - simdjson_inline simdjson_result simdjson_result::get_array() noexcept { - if (error()) { return error(); } - return first.get_array(); - } - simdjson_inline simdjson_result simdjson_result::get_object() noexcept { - if (error()) { return error(); } - return first.get_object(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); - } - simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); - } - simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); - } - simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); - } - simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); - } - template - simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(receiver, allow_replacement); - } - simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); - } - simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); - } - simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); - } - simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); - } - - template simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return first.get(); - } - template simdjson_inline error_code simdjson_result::get(T& out) noexcept { - if (error()) { return error(); } - return first.get(out); - } - - template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return std::move(first); - } - template<> simdjson_inline error_code simdjson_result::get(lasx::ondemand::value& out) noexcept { - if (error()) { return error(); } - out = first; - return SUCCESS; - } - - simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); - } - simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); - } - simdjson_inline simdjson_result simdjson_result::is_string() noexcept { - if (error()) { return error(); } - return first.is_string(); - } - simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); - } - simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); - } - simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); - } - simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); - } -#if SIMDJSON_EXCEPTIONS - template - simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return static_cast(first); - } - simdjson_inline simdjson_result::operator lasx::ondemand::array() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator lasx::ondemand::object() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator lasx::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } - simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; - } -#endif - - simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); - } - - simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { - if (error()) { return error(); } - return first.raw_json(); - } - - simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); - } - - simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { - if (error()) { return error(); } - return first.current_depth(); - } - - simdjson_inline simdjson_result simdjson_result::at_pointer( - std::string_view json_pointer) noexcept { - if (error()) { - return error(); - } - return first.at_pointer(json_pointer); - } - - simdjson_inline simdjson_result simdjson_result::at_path( - std::string_view json_path) noexcept { - if (error()) { - return error(); - } - return first.at_path(json_path); - } - -} // namespace simdjson - -#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H -/* end file simdjson/generic/ondemand/value-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 @@ -117734,1089 +166449,2283 @@ namespace simdjson { /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { - namespace lasx { - namespace ondemand { +namespace lasx { +namespace ondemand { - 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_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 value_iterator::start_object() noexcept { - SIMDJSON_TRY(start_container('{', "Not an object", "object")); - return started_object(); - } +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_object() noexcept { + SIMDJSON_TRY( start_container('{', "Not an object", "object") ); + return started_object(); +} - simdjson_warn_unused simdjson_inline simdjson_result 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 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 value_iterator::started_object() noexcept { - assert_at_container_start(); +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_object() noexcept { + assert_at_container_start(); #if SIMDJSON_DEVELOPMENT_CHECKS - _json_iter->set_start_position(_depth, start_position()); + _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(); - end_container(); - return false; - } - return true; - } + 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 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 value_iterator::started_root_object() noexcept { - auto error = check_root_object(); - if (error) { return error; } - return started_object(); - } +simdjson_warn_unused simdjson_inline simdjson_result 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 { +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"); } + 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; - } + _json_iter->ascend_to(depth()-1); + return SUCCESS; +} - simdjson_warn_unused simdjson_inline simdjson_result value_iterator::has_next_field() noexcept { - assert_at_next(); +simdjson_warn_unused simdjson_inline simdjson_result 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"); + } +} - // 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 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; - simdjson_warn_unused simdjson_inline simdjson_result 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()) { + // + // 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; } + // 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; + 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; } + // 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; } + 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; - } + } + 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; } - } + // 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; - } + // 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 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; +SIMDJSON_PUSH_DISABLE_WARNINGS +SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING +simdjson_warn_unused simdjson_inline simdjson_result 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(); + // 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; + // 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()) { + // 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; } + // 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; } + 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; } + 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 + // 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; } + // 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; - } + // 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; } + // 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 + // 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); + // 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; - } + // 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 + // 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 value_iterator::field_key() noexcept { - assert_at_next(); +simdjson_warn_unused simdjson_inline simdjson_result 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); - } + 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_value() noexcept { - assert_at_next(); +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; - } + 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 value_iterator::start_array() noexcept { - SIMDJSON_TRY(start_container('[', "Not an array", "array")); - return started_array(); - } +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_array() noexcept { + SIMDJSON_TRY( start_container('[', "Not an array", "array") ); + return started_array(); +} - simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_root_array() noexcept { - SIMDJSON_TRY(start_container('[', "Not an array", "array")); - return started_root_array(); - } +simdjson_warn_unused simdjson_inline simdjson_result 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; - } +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 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); +simdjson_warn_unused simdjson_inline simdjson_result 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()); + _json_iter->set_start_position(_depth, start_position()); #endif - return true; - } + 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 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 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 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 value_iterator::has_next_element() noexcept { - assert_at_next(); +simdjson_warn_unused simdjson_inline simdjson_result 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"); - } - } + 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 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(!not_true); - } - simdjson_warn_unused simdjson_inline simdjson_result 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 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(!not_true); +} +simdjson_warn_unused simdjson_inline simdjson_result 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 value_iterator::get_string(bool allow_replacement) noexcept { - return get_raw_json_string().unescape(json_iter(), allow_replacement); - } - template - simdjson_warn_unused simdjson_inline error_code value_iterator::get_string(string_type& receiver, bool allow_replacement) noexcept { - std::string_view content; - auto err = get_string(allow_replacement).get(content); - if (err) { return err; } - receiver = content; - return SUCCESS; - } - simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_wobbly_string() noexcept { - return get_raw_json_string().unescape_wobbly(json_iter()); - } - simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_raw_json_string() noexcept { - auto json = peek_scalar("string"); - if (*json != '"') { return incorrect_type_error("Not a string"); } - advance_scalar("string"); - return raw_json_string(json + 1); - } - simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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 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 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 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 value_iterator::is_integer() noexcept { - return numberparsing::is_integer(peek_non_root_scalar("integer")); - } - simdjson_inline simdjson_result value_iterator::get_number_type() noexcept { - return numberparsing::get_number_type(peek_non_root_scalar("integer")); - } - simdjson_inline simdjson_result 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_warn_unused simdjson_inline simdjson_result value_iterator::get_string(bool allow_replacement) noexcept { + return get_raw_json_string().unescape(json_iter(), allow_replacement); +} +template +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; } + 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 value_iterator::get_wobbly_string() noexcept { + return get_raw_json_string().unescape_wobbly(json_iter()); +} +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_raw_json_string() noexcept { + auto json = peek_scalar("string"); + if (*json != '"') { return incorrect_type_error("Not a string"); } + advance_scalar("string"); + return raw_json_string(json+1); +} +simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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 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 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 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 value_iterator::is_integer() noexcept { + return numberparsing::is_integer(peek_non_root_scalar("integer")); +} +simdjson_inline simdjson_result value_iterator::get_number_type() noexcept { + return numberparsing::get_number_type(peek_non_root_scalar("integer")); +} +simdjson_inline simdjson_result 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 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 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 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.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 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.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 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 - 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; - auto err = get_root_string(check_trailing, allow_replacement).get(content); - if (err) { return err; } - receiver = content; - return SUCCESS; - } - simdjson_warn_unused simdjson_inline simdjson_result 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 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; } - advance_scalar("string"); - return raw_json_string(json + 1); - } - simdjson_warn_unused simdjson_inline simdjson_result 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 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 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; - } +simdjson_inline simdjson_result 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.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 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.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 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 +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; } + 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 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 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; } + advance_scalar("string"); + return raw_json_string(json+1); +} +simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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(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 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 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.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; - } + 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 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.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 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.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 value_iterator::get_root_bool(bool check_trailing) noexcept { - auto max_len = peek_root_length(); - auto json = peek_root_scalar("bool"); - uint8_t tmpbuf[5 + 1 + 1]; // +1 for null termination - tmpbuf[5 + 1] = '\0'; // make sure that buffer is always null terminated. - if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 5 + 1)) { return incorrect_type_error("Not a boolean"); } - auto result = parse_bool(tmpbuf); - if (result.error() == SUCCESS) { - if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; } - advance_root_scalar("bool"); - } - return result; - } - simdjson_inline simdjson_result 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"); - } - return result; - } +simdjson_warn_unused simdjson_inline simdjson_result 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.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 error_code value_iterator::skip_child() noexcept { - SIMDJSON_ASSUME(_json_iter->token._position > _start_position); - SIMDJSON_ASSUME(_json_iter->_depth >= _depth); +simdjson_warn_unused simdjson_inline simdjson_result 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; +} - return _json_iter->skip_child(depth()); - } +simdjson_inline simdjson_result 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_inline value_iterator value_iterator::child() const noexcept { - assert_at_child(); - return { _json_iter, depth() + 1, _json_iter->token.position() }; - } +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 ); - // 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 + return _json_iter->skip_child(depth()); +} - simdjson_inline bool value_iterator::at_end() const noexcept { - return _json_iter->at_end(); - } +simdjson_inline value_iterator value_iterator::child() const noexcept { + assert_at_child(); + return { _json_iter, depth()+1, _json_iter->token.position() }; +} - simdjson_inline bool value_iterator::at_start() const noexcept { - return _json_iter->token.position() == start_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_first_field() const noexcept { - SIMDJSON_ASSUME(_json_iter->token._position > _start_position); - return _json_iter->token.position() == start_position() + 1; - } +simdjson_inline bool value_iterator::at_end() const noexcept { + return _json_iter->at_end(); +} - simdjson_inline void value_iterator::abandon() noexcept { - _json_iter->abandon(); - } +simdjson_inline bool value_iterator::at_start() const noexcept { + return _json_iter->token.position() == start_position(); +} - 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 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 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 void value_iterator::abandon() noexcept { + _json_iter->abandon(); +} - 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(); } +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; +} - // 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 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 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; } +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(); - _json_iter->return_current_and_advance(); - _json_iter->ascend_to(depth() - 1); - } + // 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 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()) { +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; } + 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(); - } + 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; - } + 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(); } +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_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(); - } + 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; } +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_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); - } + 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 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_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. - return _depth == _json_iter->_depth && *_json_iter->peek() == '"'; - } +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; - } +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_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_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); - } +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_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::move_at_container_start() noexcept { + _json_iter->_depth = _depth; + _json_iter->token.set_position(_start_position + 1); +} - simdjson_inline simdjson_result value_iterator::reset_array() noexcept { - if (error()) { return error(); } - move_at_container_start(); - return started_array(); - } +simdjson_inline simdjson_result value_iterator::reset_array() noexcept { + if(error()) { return error(); } + move_at_container_start(); + return started_array(); +} - simdjson_inline simdjson_result value_iterator::reset_object() noexcept { - if (error()) { return error(); } - move_at_container_start(); - return started_object(); - } +simdjson_inline simdjson_result 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_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_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_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); - } +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 bool value_iterator::is_valid() const noexcept { + return _json_iter != nullptr; +} - simdjson_inline simdjson_result 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 TAPE_ERROR; - } - } +simdjson_inline simdjson_result 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::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::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::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 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); - } +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 ondemand +} // namespace lasx } // namespace simdjson namespace simdjson { - simdjson_inline simdjson_result::simdjson_result(lasx::ondemand::value_iterator&& value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} - simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} +simdjson_inline simdjson_result::simdjson_result(lasx::ondemand::value_iterator &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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 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 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 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 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 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 to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result 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 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 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&& x) { + if (x.error()) { throw simdjson::simdjson_error(x.error()); } + return (out << x.value()); +} +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& 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 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 emails; }; + * + * std::string email; + * path_accessor::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 +#include +#include + +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 +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 +consteval auto make_field_step(const char (&name)[N]) { + return path_step(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 +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 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 +struct path_accessor { + using value = ::simdjson::lasx::ondemand::value; + + static constexpr auto parser = json_path_parser(); + 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 + static inline simdjson_result access(DocOrValue& doc_or_val) noexcept { + // Validate path at compile time if T is a struct + if constexpr (std::is_class_v) { + 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(doc_or_val.get_value()); + } + + // Extract value at path directly into target with compile-time type validation + // Example: std::string name; path_accessor::extract_field(doc, name); + template + static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept { + static_assert(std::is_class_v, "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(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 + static consteval std::enable_if_t, 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 + static inline error_code extract_with_reflection(simdjson_result 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(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(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_value, target_ref); + } + } + } + // Skip unexpected characters and continue + else { + return extract_with_reflection(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 + static inline simdjson_result access_impl(simdjson_result 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_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_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_value); + } + } else { + return access_impl(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(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 + 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 + 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 + static consteval bool is_container_type() { + using BaseType = std::remove_cvref_t; + if constexpr (requires { typename BaseType::value_type; }) { + return true; + } + if constexpr (std::is_array_v) { + return true; + } + return false; + } + + // Extract element type from non-reflected container + template + using extract_element_type = std::conditional_t< + requires { typename std::remove_cvref_t::value_type; }, + typename std::remove_cvref_t::value_type, + std::conditional_t< + std::is_array_v>, + std::remove_extent_t>, + void + > + >; + + // Validate path matches struct definition + static consteval bool validate_path() { + if constexpr (!std::is_class_v) { + 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 +inline simdjson_result<::simdjson::lasx::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = path_accessor; + return accessor::access(doc_or_val); +} + +// Overload without type parameter (no validation) +template +inline simdjson_result<::simdjson::lasx::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = path_accessor; + return accessor::access(doc_or_val); +} + +// ============================================================================ +// JSON Pointer Compile-Time Support (RFC 6901) +// ============================================================================ + +// JSON Pointer parser: /field/0/nested (slash-separated) +template +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 +struct pointer_accessor { + using parser = json_pointer_parser; + 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) { + 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::is_array_like_reflected(current_type)) { + return false; + } + current_type = path_accessor::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 + static inline simdjson_result access_impl(simdjson_result 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(next_value); + } else { + auto obj = current.get_object().value_unsafe(); + auto next_value = obj.find_field_unordered(token); + return access_impl(next_value); + } + } + } + + // Access JSON value at pointer + template + static inline simdjson_result access(DocOrValue& doc_or_val) noexcept { + if constexpr (std::is_class_v) { + 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 current = doc_or_val.get_value(); + return access_impl<0>(current); + } + + // Extract value at pointer directly into target with type validation + template + static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept { + static_assert(std::is_class_v, "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 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 + static consteval std::enable_if_t, 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::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 +inline simdjson_result<::simdjson::lasx::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = pointer_accessor; + return accessor::access(doc_or_val); +} + +// Overload without type parameter (no validation) +template +inline simdjson_result<::simdjson::lasx::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = pointer_accessor; + 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 */ @@ -118827,10 +168736,13581 @@ namespace simdjson { #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 + +#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 +#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); +} + +simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, + uint64_t *result) { + return __builtin_uaddll_overflow(value1, value2, + reinterpret_cast(result)); +} + +} // 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 + struct simd8; + + // SIMD byte mask type (returned by things like eq and gt) + template<> + struct simd8 { + 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 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 operator|(const simd8 other) const { return __riscv_vmor(*this, other, VL8); } + simdjson_inline simd8 operator&(const simd8 other) const { return __riscv_vmand(*this, other, VL8); } + simdjson_inline simd8 operator^(const simd8 other) const { return __riscv_vmxor(*this, other, VL8); } + simdjson_inline simd8 bit_andnot(const simd8 other) const { return __riscv_vmandn(other, *this, VL8); } + simdjson_inline simd8 operator~() const { return __riscv_vmnot(*this, VL8); } + simdjson_inline simd8& operator|=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast | other; return *this_cast; } + simdjson_inline simd8& operator&=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast & other; return *this_cast; } + simdjson_inline simd8& operator^=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast ^ other; return *this_cast; } + }; + + // Unsigned bytes + template<> + struct simd8 { + + 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 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 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( + 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 operator|(const simd8 other) const { return __riscv_vor_vv_u8m1( value, other, VL8); } + simdjson_inline simd8 operator&(const simd8 other) const { return __riscv_vand_vv_u8m1( value, other, VL8); } + simdjson_inline simd8 operator^(const simd8 other) const { return __riscv_vxor_vv_u8m1( value, other, VL8); } + simdjson_inline simd8 operator~() const { return __riscv_vnot_v_u8m1(value, VL8); } +#if __riscv_zvbb + simdjson_inline simd8 bit_andnot(const simd8 other) const { return __riscv_vandn_vv_u8m1(other, value, VL8); } +#else + simdjson_inline simd8 bit_andnot(const simd8 other) const { return other & ~*this; } +#endif + simdjson_inline simd8& operator|=(const simd8 other) { value = *this | other; return *this; } + simdjson_inline simd8& operator&=(const simd8 other) { value = *this & other; return *this; } + simdjson_inline simd8& operator^=(const simd8 other) { value = *this ^ other; return *this; } + + simdjson_inline simd8 operator==(const simd8 other) const { return __riscv_vmseq(value, other, VL8); } + simdjson_inline simd8 operator==(uint8_t other) const { return __riscv_vmseq(value, other, VL8); } + + template + simdjson_inline simd8 prev(const simd8 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 saturating_add(const simd8 other) const { return __riscv_vsaddu(value, other, VL8); } + simdjson_inline simd8 saturating_sub(const simd8 other) const { return __riscv_vssubu(value, other, VL8); } + + // Addition/subtraction are the same for signed and unsigned + simdjson_inline simd8 operator+(const simd8 other) const { return __riscv_vadd(value, other, VL8); } + simdjson_inline simd8 operator-(const simd8 other) const { return __riscv_vsub(value, other, VL8); } + simdjson_inline simd8& operator+=(const simd8 other) { value = *this + other; return *this; } + simdjson_inline simd8& operator-=(const simd8 other) { value = *this - other; return *this; } + + // Order-specific operations + simdjson_inline simd8 operator<=(const simd8 other) const { return __riscv_vmsleu(value, other, VL8); } + simdjson_inline simd8 operator>=(const simd8 other) const { return __riscv_vmsgeu(value, other, VL8); } + simdjson_inline simd8 operator<(const simd8 other) const { return __riscv_vmsltu(value, other, VL8); } + simdjson_inline simd8 operator>(const simd8 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 gt_bits(const simd8 other) const { return simd8(*this > other); } + // Same as <, but instead of guaranteeing all 1's == true, false = 0 and true = nonzero. + simdjson_inline simd8 lt_bits(const simd8 other) const { return simd8(*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 bits) const { return (*this & bits).any_bits_set_anywhere(); } + template + simdjson_inline simd8 shr() const { return __riscv_vsrl(value, N, VL8); } + template + simdjson_inline simd8 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 + simdjson_inline simd8 lookup_16(simd8 lookup_table) const { + return __riscv_vrgather(lookup_table, value, VL8); + } + + // compress inactive elements, to match AVX-512 behavior + template + 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 + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; + + // Signed bytes + template<> + struct simd8 { + 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 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( + 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() const { return __riscv_vreinterpret_u8m1(value); } + + // Math + simdjson_inline simd8 operator+(const simd8 other) const { return __riscv_vadd(value, other, VL8); } + simdjson_inline simd8 operator-(const simd8 other) const { return __riscv_vsub(value, other, VL8); } + simdjson_inline simd8& operator+=(const simd8 other) { value = *this + other; return *this; } + simdjson_inline simd8& operator-=(const simd8 other) { value = *this - other; return *this; } + + // Order-sensitive comparisons + simdjson_inline simd8 max_val( const simd8 other) const { return __riscv_vmax( value, other, VL8); } + simdjson_inline simd8 min_val( const simd8 other) const { return __riscv_vmin( value, other, VL8); } + simdjson_inline simd8 operator>( const simd8 other) const { return __riscv_vmsgt(value, other, VL8); } + simdjson_inline simd8 operator<( const simd8 other) const { return __riscv_vmslt(value, other, VL8); } + simdjson_inline simd8 operator==(const simd8 other) const { return __riscv_vmseq(value, other, VL8); } + + template + simdjson_inline simd8 prev(const simd8 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 + simdjson_inline simd8 lookup_16(simd8 lookup_table) const { + return __riscv_vrgather(lookup_table, value, VL8); + } + template + simdjson_inline simd8 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::repeat_16( + replace0, replace1, replace2, replace3, + replace4, replace5, replace6, replace7, + replace8, replace9, replace10, replace11, + replace12, replace13, replace14, replace15 + )); + } + }; + + template + struct simd8x64; + template<> + struct simd8x64 { + static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); + vuint8x64_t value; + +#if __riscv_v_fixed_vlen >= 512 + template simd8 get() const { return value; } +#else + template simd8 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& o) = delete; // no copy allowed + simd8x64& operator=(const simd8& 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 + +} // 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); + 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 v(src); + v.store(dst); + return { (v == '\\').to_bitmask(), (v == '"').to_bitmask() }; +} + +struct escaping { + static constexpr uint64_t BYTES_PROCESSED = sizeof(simd8); + 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 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 + +#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(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_BUILDER_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; + +} // 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 struct is_builtin_deserializable : std::false_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; + +template +concept is_builtin_deserializable_v = is_builtin_deserializable::value; + +template +concept custom_deserializable = tag_invocable; + +template +concept deserializable = custom_deserializable || is_builtin_deserializable_v || concepts::optional_type; + +template +concept nothrow_custom_deserializable = nothrow_tag_invocable; + +// built-in types are noexcept and if an error happens, the value simply gets ignored and the error is returned. +template +concept nothrow_deserializable = nothrow_custom_deserializable || is_builtin_deserializable_v; + +/// 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 + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(array_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for object + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(object_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for value + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document reference + template + requires custom_deserializable + simdjson_warn_unused constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + 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 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 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 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 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 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 has_next_field() noexcept; + + /** + * Get the current field's key. + */ + simdjson_warn_unused simdjson_inline simdjson_result field_key() 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 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 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 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 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 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 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 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 get_string(bool allow_replacement) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_int64() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_double() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_bool() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result is_null() noexcept; + simdjson_warn_unused simdjson_inline bool is_negative() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result is_integer() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_number() noexcept; + + simdjson_warn_unused simdjson_inline simdjson_result get_root_string(bool check_trailing, bool allow_replacement) noexcept; + template + 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 get_root_wobbly_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_raw_json_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64_in_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_int64(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_int64_in_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_double(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_double_in_string(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_bool(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline bool is_root_negative() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result is_root_integer(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_number_type(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result get_root_number(bool check_trailing) noexcept; + simdjson_warn_unused simdjson_inline simdjson_result 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 reset_array() noexcept; + /** + * Restarts an object iteration. + * @returns Whether the object has any fields (returns false for empty). + */ + simdjson_inline simdjson_result 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 parse_null(const uint8_t *json) const noexcept; + simdjson_inline simdjson_result 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 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; + /** @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 : public rvv_vls::implementation_simdjson_result_base { +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 */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include + +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, uint64_t, int64_t, double, bool + * + * You may use get_double(), get_bool(), get_uint64(), get_int64(), + * 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 + simdjson_inline simdjson_result get() +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + #if SIMDJSON_SUPPORTS_CONCEPTS + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else if constexpr (concepts::optional_type) { + using value_type = typename std::remove_cvref_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(out.value()); + } else { + static_assert(!sizeof(T), "The get 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 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, double, or bool."); + static_cast(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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " 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(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 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 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 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 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 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 get_int64_in_string() 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 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 get_double_in_string() noexcept; + + /** + * Cast this JSON value to a string. + * + * The string is guaranteed to be valid UTF-8. + * + * Equivalent to get(). + * + * 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 get_string(bool allow_replacement = false) noexcept; + + /** + * 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 + 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 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 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 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 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 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 + 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 signed 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(). + * + * @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 begin() & noexcept; + /** + * Sentinel representing the end of the array. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result 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 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 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 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 find_field(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ + simdjson_inline simdjson_result 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 find_field_unordered(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result find_field_unordered(const char *key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result 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 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 is_scalar() noexcept; + /** + * Checks whether the value is a string. + * + * @returns true if the type is string + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + */ + simdjson_inline simdjson_result is_string() noexcept; + + /** + * Checks whether the value is a negative number. + * + * @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 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 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 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 raw_json() noexcept; + + /** + * Returns the current location in the document if in bounds. + */ + simdjson_inline simdjson_result 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 at_pointer(std::string_view json_pointer) noexcept; + + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + simdjson_inline simdjson_result at_path(std::string_view at_path) noexcept; + + /** + * Get all values matching the given JSONPath expression with wildcard support. + * Supports wildcard character (*) for arrays or ".*" for objects. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern + */ + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) 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 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; + friend struct simdjson_result; + friend class field; +}; + +} // namespace ondemand +} // namespace rvv_vls +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public rvv_vls::implementation_simdjson_result_base { +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 get_array() noexcept; + simdjson_inline simdjson_result get_object() noexcept; + + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result is_null() noexcept; + + template simdjson_inline simdjson_result get() noexcept; + + template simdjson_inline error_code get(T &out) noexcept; + +#if SIMDJSON_EXCEPTIONS + template + 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 count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result 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 find_field(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ + simdjson_inline simdjson_result 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 find_field_unordered(std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result find_field_unordered(const char *key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ + simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result 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 type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + simdjson_inline simdjson_result is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + + /** @copydoc simdjson_inline std::string_view value::raw_json_token() const noexcept */ + simdjson_inline simdjson_result raw_json_token() noexcept; + simdjson_inline simdjson_result raw_json() noexcept; + + /** @copydoc simdjson_inline simdjson_result current_location() noexcept */ + simdjson_inline simdjson_result current_location() noexcept; + /** @copydoc simdjson_inline int32_t current_depth() const noexcept */ + simdjson_inline simdjson_result current_depth() const noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) 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 +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 +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 (`{` `}` `[` `]` `,` `:` `""` `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 value_iterator; + friend class object; + template + friend simdjson_inline void logger::log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta, logger::log_level level, Args&&... args) noexcept; + template + 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 : public rvv_vls::implementation_simdjson_result_base { +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}; + +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; + + /** + * 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 unescape(raw_json_string in, bool allow_replacement) noexcept; + simdjson_inline simdjson_result 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 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 + 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 + 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 : public rvv_vls::implementation_simdjson_result_base { +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 + 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 + 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 &type) noexcept(false); +#endif + +} // namespace ondemand +} // namespace rvv_vls +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public rvv_vls::implementation_simdjson_result_base { +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 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 unescape_wobbly(json_iterator &iter) const noexcept; + const uint8_t * buf{}; + friend class object; + friend class field; + friend class parser; + friend struct simdjson_result; +}; + +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 : public rvv_vls::implementation_simdjson_result_base { +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 raw() const noexcept; + simdjson_inline char operator[](size_t) const noexcept; + simdjson_inline simdjson_warn_unused simdjson_result unescape(rvv_vls::ondemand::json_iterator &iter, bool allow_replacement) const noexcept; + simdjson_inline simdjson_warn_unused simdjson_result 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 +#include + +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 iterate(padded_string_view json) & noexcept; +#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON + simdjson_warn_unused simdjson_result iterate_allow_incomplete_json(padded_string_view json) & noexcept; +#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const char *json, size_t len, size_t capacity) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const uint8_t *json, size_t len, size_t capacity) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(std::string_view json, size_t capacity) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const std::string &json) & noexcept; + /** @overload simdjson_result 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 iterate(std::string &json) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const simdjson_result &json) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result iterate(const simdjson_result &json) & noexcept; + /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ + simdjson_warn_unused simdjson_result 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 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 10MB, which has been a reasonable sweet spot in our tests. + * @param allow_comma_separated (defaults on false) This allows a mode where the documents are + * separated by commas instead of whitespace. It comes with a performance + * penalty because the entire document is indexed at once (and the document must be + * less than 4 GB), and there is no multithreading. In this mode, the batch_size parameter + * is effectively ignored, as it is set to at least the document size. + * @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 iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(padded_string_view json, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_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 iterate_many(std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ + inline simdjson_result iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept; + /** @private We do not want to allow implicit conversion from C string to std::string. */ + simdjson_result iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete; + + /** 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 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 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: + friend bool release_parser(); + friend ondemand::parser& get_parser(); + /** Get the thread-local parser instance, allocates it if needed */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_parser_instance(); + /** Get the thread-local parser instance, it might be null */ + static simdjson_inline simdjson_warn_unused std::unique_ptr& get_threadlocal_parser_if_exists(); + /** @private [for benchmarking access] The implementation to use */ + std::unique_ptr implementation{}; + size_t _capacity{0}; + size_t _max_capacity; + size_t _max_depth{DEFAULT_MAX_DEPTH}; + std::unique_ptr string_buf{}; + +#if SIMDJSON_DEVELOPMENT_CHECKS + std::unique_ptr start_positions{}; +#endif + + friend class json_iterator; + friend class document_stream; +}; + +} // namespace ondemand +} // namespace rvv_vls +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public rvv_vls::implementation_simdjson_result_base { +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 */ +/* 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. + */ + simdjson_inline simdjson_result begin() noexcept; + /** + * Sentinel representing the end of the array. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result 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 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 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 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 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 at_path(std::string_view json_path) noexcept; + + /** + * Get all values matching the given JSONPath expression with wildcard support. + * Supports wildcard patterns like "[*]" to match all array elements. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern + */ + inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + + /** + * Consumes the array and returns a string_view instance corresponding to the + * array as represented in JSON. It points inside the original document. + */ + simdjson_inline simdjson_result 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 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + 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 + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(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 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 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 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{}; + + friend class value; + friend class document; + friend struct simdjson_result; + friend struct simdjson_result; + friend class array_iterator; +}; + +} // namespace ondemand +} // namespace rvv_vls +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public rvv_vls::implementation_simdjson_result_base { +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 begin() noexcept; + simdjson_inline simdjson_result end() noexcept; + inline simdjson_result count_elements() & noexcept; + inline simdjson_result is_empty() & noexcept; + inline simdjson_result reset() & noexcept; + simdjson_inline simdjson_result at(size_t index) noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h + + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(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 */ +/* 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; + 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; + + // + // Iterator interface + // + + /** + * Get the current element. + * + * Part of the std::iterator interface. + */ + simdjson_inline simdjson_result + 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}; +#endif + value_iterator iter{}; + + simdjson_inline array_iterator(const value_iterator &iter) noexcept; + + friend class array; + friend class value; + friend struct simdjson_result; +}; + +} // namespace ondemand +} // namespace rvv_vls +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public rvv_vls::implementation_simdjson_result_base { + using iterator_category = std::input_iterator_tag; + using value_type = simdjson_result; + 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 operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline bool operator==(const simdjson_result &) const noexcept; + simdjson_inline bool operator!=(const simdjson_result &) const noexcept; + simdjson_inline simdjson_result &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 */ +/* 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 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 get_object() & noexcept; + /** + * Cast this JSON value to an unsigned integer. + * + * @returns A signed 64-bit integer. + * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. + */ + simdjson_inline simdjson_result get_uint64() noexcept; + /** + * Cast this JSON value (inside string) to an unsigned integer. + * + * @returns A signed 64-bit integer. + * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. + */ + simdjson_inline simdjson_result 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 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 get_int64_in_string() 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 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 get_double_in_string() noexcept; + /** + * 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 get_string(bool allow_replacement = false) noexcept; + /** + * 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 + 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 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 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 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 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 is_null() noexcept; + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool + * + * You may use get_double(), get_bool(), get_uint64(), get_int64(), + * 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 + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + /** + * @overload template simdjson_result 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 + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::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(*this).get(); + } + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_CONCEPTS + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { + static_assert(!sizeof(T), "The get 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 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, double, or bool."); + static_cast(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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " 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(out); // to get rid of unused errors + return UNINITIALIZED; +#endif // SIMDJSON_SUPPORTS_CONCEPTS + } + + /** @overload template error_code get(T &out) & noexcept */ + template simdjson_deprecated simdjson_inline error_code get(T &out) && noexcept; + +#if SIMDJSON_EXCEPTIONS + /** + * Cast this JSON value to an instance of type T. The programmer is responsible for + * providing an implementation of get 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 + explicit simdjson_inline operator T() & noexcept(false); + template + 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 signed 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. + * + * @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) 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. + */ + 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. + */ + 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 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 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 at(size_t index) & noexcept; + /** + * Begin array iteration. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result begin() & noexcept; + /** + * Sentinel representing the end of the array. + * + * Part of the std::iterable interface. + */ + simdjson_inline simdjson_result 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 find_field(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result 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 find_field_unordered(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result 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 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 is_scalar() noexcept; + + /** + * Checks whether the document is a string. + * + * @returns true if the type is string + * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". + */ + simdjson_inline simdjson_result is_string() noexcept; + + /** + * Checks whether the document is a negative number. + * + * @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 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 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 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 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 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 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 at_path(std::string_view json_path) noexcept; + + /** + * Get all values matching the given JSONPath expression with wildcard support. + * + * Supports wildcard patterns like "$.array[*]" or "$.object.*" to match multiple elements. + * + * This method materializes all matching values into a vector. + * The document will be consumed after this call. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern, or: + * - INVALID_JSON_POINTER if the JSONPath cannot be parsed + * - NO_SUCH_FIELD if a field does not exist + * - INDEX_OUT_OF_BOUNDS if an array index is out of bounds + * - INCORRECT_TYPE if path traversal encounters wrong type + */ + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + + /** + * Consumes the document and returns a string_view instance corresponding to the + * document as represented in JSON. It points inside the original byte array containing + * the JSON document. + */ + simdjson_inline simdjson_result 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 + requires(std::is_class_v && (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 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 get_array() & noexcept; + simdjson_inline simdjson_result get_object() & noexcept; + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result get_value() noexcept; + + simdjson_inline simdjson_result is_null() noexcept; + template + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + template + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::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(*this).get(); + } + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_CONCEPTS + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_CONCEPTS + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { + static_assert(!sizeof(T), "The get 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 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, double, or bool."); + static_cast(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, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " 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(out); // to get rid of unused errors + return UNINITIALIZED; +#endif // SIMDJSON_SUPPORTS_CONCEPTS + } + + /** @overload template error_code get(T &out) & noexcept */ + template simdjson_inline error_code get(T &out) && noexcept; + simdjson_inline simdjson_result raw_json() noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (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 + explicit simdjson_inline operator T() noexcept(false); + simdjson_inline operator array() & noexcept(false); + simdjson_inline operator 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 raw_json_string() noexcept(false); + simdjson_inline operator bool() noexcept(false); + simdjson_inline operator value() noexcept(false); +#endif + simdjson_inline simdjson_result count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) & noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result end() & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(const char *key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + + simdjson_inline simdjson_result type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + + simdjson_inline simdjson_result current_location() noexcept; + simdjson_inline int32_t current_depth() const noexcept; + simdjson_inline bool is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + simdjson_inline simdjson_result raw_json_token() noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + +private: + document *doc{nullptr}; +}; +} // namespace ondemand +} // namespace rvv_vls +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public rvv_vls::implementation_simdjson_result_base { +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 get_array() & noexcept; + simdjson_inline simdjson_result get_object() & noexcept; + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result get_value() noexcept; + simdjson_inline simdjson_result is_null() noexcept; + + template simdjson_inline simdjson_result get() & noexcept; + template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept; + + template simdjson_inline error_code get(T &out) & noexcept; + template simdjson_inline error_code get(T &out) && noexcept; +#if SIMDJSON_EXCEPTIONS + + using rvv_vls::implementation_simdjson_result_base::operator*; + using rvv_vls::implementation_simdjson_result_base::operator->; + template ::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); + 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); + simdjson_inline operator rvv_vls::ondemand::value() noexcept(false); +#endif + simdjson_inline simdjson_result count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) & noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result end() & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(const char *key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + simdjson_inline simdjson_result type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + simdjson_inline simdjson_result current_location() noexcept; + simdjson_inline int32_t current_depth() const noexcept; + simdjson_inline bool at_end() const noexcept; + simdjson_inline bool is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + /** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */ + simdjson_inline simdjson_result raw_json_token() noexcept; + + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (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 : public rvv_vls::implementation_simdjson_result_base { +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 get_array() & noexcept; + simdjson_inline simdjson_result get_object() & noexcept; + simdjson_inline simdjson_result get_uint64() noexcept; + simdjson_inline simdjson_result get_uint64_in_string() noexcept; + simdjson_inline simdjson_result get_int64() noexcept; + simdjson_inline simdjson_result get_int64_in_string() noexcept; + simdjson_inline simdjson_result get_double() noexcept; + simdjson_inline simdjson_result get_double_in_string() noexcept; + simdjson_inline simdjson_result get_string(bool allow_replacement = false) noexcept; + template + simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result get_wobbly_string() noexcept; + simdjson_inline simdjson_result get_raw_json_string() noexcept; + simdjson_inline simdjson_result get_bool() noexcept; + simdjson_inline simdjson_result get_value() noexcept; + simdjson_inline simdjson_result is_null() noexcept; + + template simdjson_inline simdjson_result get() & noexcept; + template simdjson_inline simdjson_result get() && noexcept; + + template simdjson_inline error_code get(T &out) & noexcept; + template simdjson_inline error_code get(T &out) && noexcept; +#if SIMDJSON_EXCEPTIONS + template + 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); + simdjson_inline operator rvv_vls::ondemand::value() noexcept(false); +#endif + simdjson_inline simdjson_result count_elements() & noexcept; + simdjson_inline simdjson_result count_fields() & noexcept; + simdjson_inline simdjson_result at(size_t index) & noexcept; + simdjson_inline simdjson_result begin() & noexcept; + simdjson_inline simdjson_result end() & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(const char *key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; + simdjson_inline simdjson_result type() noexcept; + simdjson_inline simdjson_result is_scalar() noexcept; + simdjson_inline simdjson_result is_string() noexcept; + simdjson_inline simdjson_result current_location() noexcept; + simdjson_inline simdjson_result current_depth() const noexcept; + simdjson_inline simdjson_result is_negative() noexcept; + simdjson_inline simdjson_result is_integer() noexcept; + simdjson_inline simdjson_result get_number_type() noexcept; + simdjson_inline simdjson_result get_number() noexcept; + /** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */ + simdjson_inline simdjson_result raw_json_token() noexcept; + + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; +#if SIMDJSON_STATIC_REFLECTION + template + requires(std::is_class_v && (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 +#include +#include +#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(); + * + */ + inline size_t truncated_bytes() const noexcept; + + class iterator { + public: + using value_type = simdjson_result; + using reference = simdjson_result; + 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. + * + * 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) + */ + simdjson_inline document_stream( + ondemand::parser &parser, + const uint8_t *buf, + size_t len, + size_t batch_size, + bool allow_comma_separated + ) 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. */ + inline void next_document() 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; + /** + * 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 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. + */ + 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; + friend struct simdjson::internal::simdjson_result_base; +}; // document_stream + +} // namespace ondemand +} // namespace rvv_vls +} // namespace simdjson + +namespace simdjson { +template<> +struct simdjson_result : public rvv_vls::implementation_simdjson_result_base { +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 so you can use C++ algorithms that rely on pairs. + */ +class field : public std::pair { +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 unescaped_key(bool allow_replacement = false) 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. 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 + 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; + /** + * 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 start(value_iterator &parent_iter) noexcept; + static simdjson_inline simdjson_result start(const value_iterator &parent_iter, raw_json_string key) noexcept; + friend struct simdjson_result; + friend class object_iterator; +}; + +} // namespace ondemand +} // namespace rvv_vls +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public rvv_vls::implementation_simdjson_result_base { +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 unescaped_key(bool allow_replacement = false) noexcept; + template + simdjson_inline error_code unescaped_key(string_type &receiver, bool allow_replacement = false) noexcept; + simdjson_inline simdjson_result key() noexcept; + simdjson_inline simdjson_result key_raw_json_token() noexcept; + simdjson_inline simdjson_result escaped_key() noexcept; + simdjson_inline simdjson_result value() noexcept; +}; + +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ONDEMAND_FIELD_H +/* end file simdjson/generic/ondemand/field.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 */ +/* 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 { + +/** + * 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 + */ + simdjson_inline simdjson_result begin() noexcept; + simdjson_inline simdjson_result 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"); + * ``` + * 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 find_field(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result 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 find_field_unordered(std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_inline simdjson_result operator[](std::string_view key) && 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("/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 at_pointer(std::string_view json_pointer) noexcept; + + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + inline simdjson_result at_path(std::string_view json_path) noexcept; + + /** + * Get all values matching the given JSONPath expression with wildcard support. + * Supports wildcard patterns like ".*" to match all object fields. + * + * @param json_path JSONPath expression with wildcards + * @return Vector of values matching the wildcard pattern + */ + inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + + /** + * Reset the iterator so that we are pointing back at the + * beginning of the object. You should still consume values only once even if you + * 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 reset() & 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 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 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 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 + simdjson_warn_unused simdjson_inline error_code get(T &out) + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) { + static_assert(custom_deserializable); + 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 + simdjson_inline simdjson_result get() + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(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 + requires(std::is_class_v && (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 start(value_iterator &iter) noexcept; + static simdjson_inline simdjson_result start_root(value_iterator &iter) noexcept; + static simdjson_inline simdjson_result 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{}; + + friend class value; + friend class document; + friend struct simdjson_result; +}; + +} // namespace ondemand +} // namespace rvv_vls +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public rvv_vls::implementation_simdjson_result_base { +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 begin() noexcept; + simdjson_inline simdjson_result end() noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field(std::string_view key) && noexcept; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; + simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; + simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; + simdjson_inline simdjson_result at_path(std::string_view json_path) noexcept; + simdjson_inline simdjson_result> at_path_with_wildcard(std::string_view json_path) noexcept; + inline simdjson_result reset() noexcept; + inline simdjson_result is_empty() noexcept; + inline simdjson_result count_fields() & noexcept; + inline simdjson_result raw_json() noexcept; + #if SIMDJSON_SUPPORTS_CONCEPTS + // TODO: move this code into object-inl.h + + template + simdjson_inline simdjson_result get() noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + return first; + } + return first.get(); + } + template + simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept { + if (error()) { return error(); } + if constexpr (std::is_same_v) { + out = first; + } else { + SIMDJSON_TRY( first.get(out) ); + } + return SUCCESS; + } + +#if SIMDJSON_STATIC_REFLECTION + // TODO: move this code into object-inl.h + template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) + simdjson_warn_unused simdjson_inline error_code extract_into(T& out) noexcept { + if (error()) { return error(); } + return first.extract_into(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; + + // + // Iterator interface + // + + // Reads key and value, yielding them to the user. + // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_inline simdjson_result 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}; +#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; + friend class object; +}; + +} // namespace ondemand +} // namespace rvv_vls +} // namespace simdjson + +namespace simdjson { + +template<> +struct simdjson_result : public rvv_vls::implementation_simdjson_result_base { +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 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 &) const noexcept; + // Assumes it's being compared with the end. true if depth >= iter->depth. + simdjson_inline bool operator!=(const simdjson_result &) const noexcept; + // Checks for ']' and ',' + simdjson_inline simdjson_result &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/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 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 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 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 to_json_string(rvv_vls::ondemand::array& x) noexcept; +inline simdjson_result to_json_string(simdjson_result x); +inline simdjson_result to_json_string(simdjson_result x); +inline simdjson_result to_json_string(simdjson_result x); +inline simdjson_result to_json_string(simdjson_result 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 + requires(!std::same_as && + !std::same_as && + !std::same_as && + !std::same_as) +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 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 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&& 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&& 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 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): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +#include +#if SIMDJSON_STATIC_REFLECTION +#include +// #include // for std::define_static_string - header not available yet +#endif + +namespace simdjson { + +////////////////////////////// +// Number deserialization +////////////////////////////// + +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + uint64_t x; + SIMDJSON_TRY(val.get_uint64().get(x)); + if (x > (limits::max)()) { + return NUMBER_OUT_OF_RANGE; + } + out = static_cast(x); + return SUCCESS; +} + +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + double x; + SIMDJSON_TRY(val.get_double().get(x)); + out = static_cast(x); + return SUCCESS; +} + +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + 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(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 +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v) { + std::string_view str; + SIMDJSON_TRY(val.get_string().get(str)); + out = T{str}; + return SUCCESS; +} + + +/** + * 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>(). + */ +template +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { + using value_type = typename std::remove_cvref_t::value_type; + static_assert( + deserializable, + "The specified type inside the container must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the container must default constructible."); + rvv_vls::ondemand::array arr; + if constexpr (std::is_same_v, rvv_vls::ondemand::array>) { + arr = val; + } else { + SIMDJSON_TRY(val.get_array().get(arr)); + } + + for (auto v : arr) { + if constexpr (concepts::returns_reference) { + if (auto const err = v.get().get(concepts::emplace_one(out)); + err) { + // If an error occurs, the empty element that we just inserted gets + // removed. We're not using a temp variable because if T is a heavy + // type, we want the valid path to be the fast path and the slow path be + // the path that has errors in it. + if constexpr (requires { out.pop_back(); }) { + static_cast(out.pop_back()); + } + return err; + } + } else { + value_type temp; + if (auto const err = v.get().get(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 +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { + using value_type = typename std::remove_cvref_t::mapped_type; + static_assert( + deserializable, + "The specified value type inside the container must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "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().get(this_value)); + [[maybe_unused]] std::pair 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 +error_code tag_invoke(deserialize_tag, rvv_vls::ondemand::object &obj, T &out) noexcept { + using value_type = typename std::remove_cvref_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 +error_code tag_invoke(deserialize_tag, rvv_vls::ondemand::value &val, T &out) noexcept { + rvv_vls::ondemand::object obj; + SIMDJSON_TRY(val.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} + +template +error_code tag_invoke(deserialize_tag, rvv_vls::ondemand::document &doc, T &out) noexcept { + rvv_vls::ondemand::object obj; + SIMDJSON_TRY(doc.get_object().get(obj)); + return simdjson::deserialize(obj, out); +} + +template +error_code tag_invoke(deserialize_tag, rvv_vls::ondemand::document_reference &doc, T &out) noexcept { + 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. + * + * If constructing T is nothrow, this conversion should be nothrow as well since + * we return MEMALLOC if we're not able to allocate memory instead of throwing + * the error message. + * + * @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 +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::element_type, ValT>) { + using element_type = typename std::remove_cvref_t::element_type; + + // For better error messages, don't use these as constraints on + // the tag_invoke CPO. + static_assert( + deserializable, + "The specified type inside the unique_ptr must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the unique_ptr must default constructible."); + + auto ptr = new (std::nothrow) element_type(); + if (ptr == nullptr) { + return MEMALLOC; + } + SIMDJSON_TRY(val.template get(*ptr)); + out.reset(ptr); + return SUCCESS; +} + +/** + * This CPO (Customization Point Object) will help deserialize into optional types. + */ +template +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable::value_type, decltype(val)>) { + using value_type = typename std::remove_cvref_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(out.value())); + return SUCCESS; +} + + +#if SIMDJSON_STATIC_REFLECTION + + +template +constexpr bool user_defined_type = (std::is_class_v +&& !std::is_same_v && !std::is_same_v && !concepts::optional_type && +!concepts::appendable_containers); + + +template + requires(user_defined_type && std::is_class_v) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept { + rvv_vls::ondemand::object obj; + if constexpr (std::is_same_v, rvv_vls::ondemand::object>) { + obj = val; + } else { + SIMDJSON_TRY(val.get_object().get(obj)); + } + 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)); + if constexpr (concepts::optional_type) { + // for optional members, it's ok if the key is missing + auto error = obj[key].get(out.[:mem:]); + if (error && error != NO_SUCH_FIELD) { + if(error == NO_SUCH_FIELD) { + out.[:mem:].reset(); + continue; + } + return error; + } + } else { + // for non-optional members, the key must be present + SIMDJSON_TRY(obj[key].get(out.[:mem:])); + } + } + }; + return simdjson::SUCCESS; +} + +// Support for enum deserialization - deserialize from string representation using expand approach from P2996R12 +template + requires(std::is_enum_v) +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)); + constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T)); + template for (constexpr auto enum_val : enumerators) { + if (str == std::meta::identifier_of(enum_val)) { + out = [:enum_val:]; + return SUCCESS; + } + }; + + return INCORRECT_TYPE; +#else + // Fallback: deserialize as integer if reflection not available + std::underlying_type_t int_val; + SIMDJSON_TRY(val.get(int_val)); + out = static_cast(int_val); + return SUCCESS; +#endif +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr &out) noexcept { + if (!out) { + out = std::make_unique(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +template + requires(user_defined_type>) +error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr &out) noexcept { + if (!out) { + out = std::make_shared(); + if (!out) { + return MEMALLOC; + } + } + if (auto err = val.get(*out)) { + out.reset(); + return err; + } + return SUCCESS; +} + +#endif // SIMDJSON_STATIC_REFLECTION + +//////////////////////////////////////// +// Unique pointers +//////////////////////////////////////// +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr &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(); + 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 &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_bool().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_int64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_uint64().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + if (!out) { return MEMALLOC; } + } + SIMDJSON_TRY(val.get_double().get(*out)); + return SUCCESS; +} + +error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr &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(); + 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 &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_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 &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_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 &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(); + } + int64_t temp; + SIMDJSON_TRY(val.get_int64().get(temp)); + *out = static_cast(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::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::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::started(value_iterator &iter) noexcept { + bool has_value; + SIMDJSON_TRY(iter.started_array().get(has_value)); + return array(iter); +} + +simdjson_inline simdjson_result array::begin() noexcept { +#if SIMDJSON_DEVELOPMENT_CHECKS + if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } +#endif + return array_iterator(iter); +} +simdjson_inline simdjson_result 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 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(starting_point), size_t(final_point - starting_point)); +} + +SIMDJSON_PUSH_DISABLE_WARNINGS +SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING +simdjson_inline simdjson_result array::count_elements() & 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 array after counting the number of elements. + iter.reset_array(); + return count; +} +SIMDJSON_POP_DISABLE_WARNINGS + +simdjson_inline simdjson_result array::is_empty() & noexcept { + bool is_not_empty; + auto error = iter.reset_array().get(is_not_empty); + if(error) { return error; } + return !is_not_empty; +} + +inline simdjson_result array::reset() & noexcept { + return iter.reset_array(); +} + +inline simdjson_result array::at_pointer(std::string_view json_pointer) noexcept { + if (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; } + + // Read the array index + size_t array_index = 0; + size_t i; + for (i = 0; i < json_pointer.length() && json_pointer[i] != '/'; i++) { + uint8_t digit = uint8_t(json_pointer[i] - '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; } + array_index = array_index*10 + digit; + } + + // 0 followed by other digits is invalid + if (i > 1 && json_pointer[0] == '0') { return INVALID_JSON_POINTER; } // "JSON pointer array index has other characters after 0" + + // Empty string is invalid; so is a "/" with no digits before it + if (i == 0) { return INVALID_JSON_POINTER; } // "Empty string in JSON pointer array index" + // 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 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); +} + +inline simdjson_result> array::at_path_with_wildcard(std::string_view json_path) noexcept { + std::vector result; + + 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; + // Wildcard case + if(key=="*"){ + for(auto element: *this){ + + if(element.error()){ + return element.error(); + } + + if(remaining_path.empty()){ + // Use value_unsafe() because we've already checked for errors above. + // The 'element' is a simdjson_result wrapper, and we need to extract + // the underlying value. value_unsafe() is safe here because error() returned false. + result.push_back(std::move(element).value_unsafe()); + + }else{ + auto nested_result = element.at_path_with_wildcard(remaining_path); + + if(nested_result.error()){ + return nested_result.error(); + } + // Same logic as above. + std::vector nested_matches = std::move(nested_result).value_unsafe(); + + result.insert(result.end(), + std::make_move_iterator(nested_matches.begin()), + std::make_move_iterator(nested_matches.end())); + } + } + return result; + }else{ + // Specific index case in which we access the element at the given index + size_t idx=0; + + for(char c:key){ + if(c < '0' || c > '9'){ + return INVALID_JSON_POINTER; + } + idx = idx*10 + (c - '0'); + } + + auto element = at(idx); + + if(element.error()){ + return element.error(); + } + + if(remaining_path.empty()){ + result.push_back(std::move(element).value_unsafe()); + return result; + }else{ + return element.at_path_with_wildcard(remaining_path); + } + } +} + +simdjson_inline simdjson_result array::at(size_t index) noexcept { + 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::simdjson_result( + rvv_vls::ondemand::array &&value +) noexcept + : implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept + : implementation_simdjson_result_base(error) +{ +} + +simdjson_inline simdjson_result simdjson_result::begin() noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() noexcept { + if (error()) { return error(); } + return first.end(); +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::is_empty() & noexcept { + if (error()) { return error(); } + return first.is_empty(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path(json_path); +} +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path_with_wildcard(json_path); +} +simdjson_inline simdjson_result simdjson_result::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} +{} + +simdjson_inline simdjson_result 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::simdjson_result( + rvv_vls::ondemand::array_iterator &&value +) noexcept + : rvv_vls::implementation_simdjson_result_base(std::forward(value)) +{ + first.iter.assert_is_valid(); +} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : rvv_vls::implementation_simdjson_result_base({}, error) +{ +} + +simdjson_inline simdjson_result simdjson_result::operator*() noexcept { + if (error()) { return error(); } + return *first; +} +simdjson_inline bool simdjson_result::operator==(const simdjson_result &other) const noexcept { + if (!first.iter.is_valid()) { return !error(); } + return first == other.first; +} +simdjson_inline bool simdjson_result::operator!=(const simdjson_result &other) const noexcept { + if (!first.iter.is_valid()) { return error(); } + return first != other.first; +} +simdjson_inline simdjson_result &simdjson_result::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::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 */ + +namespace simdjson { +namespace rvv_vls { +namespace ondemand { + +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 value::get_array() noexcept { + return array::start(iter); +} +simdjson_inline simdjson_result value::get_object() noexcept { + return object::start(iter); +} +simdjson_inline simdjson_result value::start_or_resume_object() noexcept { + if (iter.at_start()) { + return get_object(); + } else { + return object::resume(iter); + } +} + +simdjson_inline simdjson_result value::get_raw_json_string() noexcept { + return iter.get_raw_json_string(); +} +simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { + return iter.get_string(allow_replacement); +} +template +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 value::get_wobbly_string() noexcept { + return iter.get_wobbly_string(); +} +simdjson_inline simdjson_result value::get_double() noexcept { + return iter.get_double(); +} +simdjson_inline simdjson_result value::get_double_in_string() noexcept { + return iter.get_double_in_string(); +} +simdjson_inline simdjson_result value::get_uint64() noexcept { + return iter.get_uint64(); +} +simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { + return iter.get_uint64_in_string(); +} +simdjson_inline simdjson_result value::get_int64() noexcept { + return iter.get_int64(); +} +simdjson_inline simdjson_result value::get_int64_in_string() noexcept { + return iter.get_int64_in_string(); +} +simdjson_inline simdjson_result value::get_bool() noexcept { + return iter.get_bool(); +} +simdjson_inline simdjson_result value::is_null() noexcept { + return iter.is_null(); +} + +template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result 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); } +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(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(bool& out) noexcept { return get_bool().get(out); } + +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline value::operator T() noexcept(false) { + return get(); +} +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 value::begin() & noexcept { + return get_array().begin(); +} +simdjson_inline simdjson_result value::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result value::count_elements() & noexcept { + simdjson_result 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 value::count_fields() & noexcept { + simdjson_result answer; + auto a = get_object(); + answer = a.count_fields(); + iter.move_at_start(); + return answer; +} +simdjson_inline simdjson_result value::at(size_t index) noexcept { + auto a = get_array(); + return a.at(index); +} + +simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result value::find_field(const char *key) noexcept { + return start_or_resume_object().find_field(key); +} + +simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result value::find_field_unordered(const char *key) noexcept { + return start_or_resume_object().find_field_unordered(key); +} + +simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { + return start_or_resume_object()[key]; +} +simdjson_inline simdjson_result value::operator[](const char *key) noexcept { + return start_or_resume_object()[key]; +} + +simdjson_inline simdjson_result value::type() noexcept { + return iter.type(); +} + +simdjson_inline simdjson_result 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 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 value::is_integer() noexcept { + return iter.is_integer(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { + return iter.get_number_type(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { + return iter.get_number(); +} + +simdjson_inline std::string_view value::raw_json_token() noexcept { + return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); +} + +simdjson_inline simdjson_result 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 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::at_pointer(std::string_view json_pointer) noexcept { + 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::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; + } +} + +inline simdjson_result> value::at_path_with_wildcard(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_with_wildcard(json_path); + case json_type::object: + return (*this).get_object().at_path_with_wildcard(json_path); + default: + return INVALID_JSON_POINTER; + } +} + +} // namespace ondemand +} // namespace rvv_vls +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + rvv_vls::ondemand::value &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base(error) +{ +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + if (error()) { return error(); } + return {}; +} + +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) noexcept { + if (error()) { return error(); } + return first.find_field(key); +} + +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} + +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) noexcept { + if (error()) { return error(); } + return first[key]; +} + +simdjson_inline simdjson_result simdjson_result::get_array() noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} + +template<> simdjson_inline error_code simdjson_result::get(rvv_vls::ondemand::value &out) noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} + +template simdjson_inline simdjson_result simdjson_result::get() noexcept { + if (error()) { return error(); } + return first.get(); +} +template simdjson_inline error_code simdjson_result::get(T &out) noexcept { + if (error()) { return error(); } + return first.get(out); +} + +template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { + if (error()) { return error(); } + return std::move(first); +} + +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline simdjson_result::operator T() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first.get(); +} +simdjson_inline simdjson_result::operator rvv_vls::ondemand::array() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator rvv_vls::ondemand::object() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator rvv_vls::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { + if (error()) { return error(); } + return first.raw_json(); +} + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { + if (error()) { return error(); } + return first.current_depth(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer( + std::string_view json_pointer) noexcept { + if (error()) { + return error(); + } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + +inline simdjson_result> simdjson_result::at_path_with_wildcard( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path_with_wildcard(json_path); +} + +} // 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 */ + +namespace simdjson { +namespace rvv_vls { +namespace ondemand { + +simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept + : iter{std::forward(_iter)} +{ + logger::log_start_value(iter, "document"); +} + +simdjson_inline document document::start(json_iterator &&iter) noexcept { + return document(std::forward(iter)); +} + +inline void document::rewind() noexcept { + iter.rewind(); +} + +inline std::string document::to_debug_string() noexcept { + return iter.to_string(); +} + +inline simdjson_result 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 document::start_or_resume_object() noexcept { + if (iter.at_root()) { + return get_object(); + } else { + return object::resume(resume_value_iterator()); + } +} +simdjson_inline simdjson_result document::get_value() noexcept { + // Make sure we start any arrays or objects before returning, so that start_root_() + // 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 document::get_array() & noexcept { + auto value = get_root_value_iterator(); + return array::start_root(value); +} +simdjson_inline simdjson_result 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 document::get_uint64() noexcept { + return get_root_value_iterator().get_root_uint64(true); +} +simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { + return get_root_value_iterator().get_root_uint64_in_string(true); +} +simdjson_inline simdjson_result document::get_int64() noexcept { + return get_root_value_iterator().get_root_int64(true); +} +simdjson_inline simdjson_result document::get_int64_in_string() noexcept { + return get_root_value_iterator().get_root_int64_in_string(true); +} +simdjson_inline simdjson_result document::get_double() noexcept { + return get_root_value_iterator().get_root_double(true); +} +simdjson_inline simdjson_result document::get_double_in_string() noexcept { + return get_root_value_iterator().get_root_double_in_string(true); +} +simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { + return get_root_value_iterator().get_root_string(true, allow_replacement); +} +template +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 document::get_wobbly_string() noexcept { + return get_root_value_iterator().get_root_wobbly_string(true); +} +simdjson_inline simdjson_result document::get_raw_json_string() noexcept { + return get_root_value_iterator().get_root_raw_json_string(true); +} +simdjson_inline simdjson_result document::get_bool() noexcept { + return get_root_value_iterator().get_root_bool(true); +} +simdjson_inline simdjson_result document::is_null() noexcept { + return get_root_value_iterator().is_root_null(true); +} + +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result 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); } +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(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(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 document::get() && noexcept { return get_raw_json_string(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_value(); } + +#if SIMDJSON_EXCEPTIONS +template +simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } +template +simdjson_inline document::operator T() & noexcept(false) { return get(); } +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 document::count_elements() & noexcept { + auto a = get_array(); + simdjson_result 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 document::count_fields() & noexcept { + auto a = get_object(); + simdjson_result 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 document::at(size_t index) & noexcept { + auto a = get_array(); + return a.at(index); +} +simdjson_inline simdjson_result document::begin() & noexcept { + return get_array().begin(); +} +simdjson_inline simdjson_result document::end() & noexcept { + return {}; +} + +simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field(const char *key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::find_field_unordered(const char *key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { + return start_or_resume_object()[key]; +} +simdjson_inline simdjson_result 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 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(starting_point), size_t(final_point - starting_point)); +} + +simdjson_inline simdjson_result document::type() noexcept { + return get_root_value_iterator().type(); +} + +simdjson_inline simdjson_result 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 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 document::is_integer() noexcept { + return get_root_value_iterator().is_root_integer(true); +} + +simdjson_inline simdjson_result document::get_number_type() noexcept { + return get_root_value_iterator().get_root_number_type(true); +} + +simdjson_inline simdjson_result document::get_number() noexcept { + return get_root_value_iterator().get_root_number(true); +} + + +simdjson_inline simdjson_result document::raw_json_token() noexcept { + auto _iter = get_root_value_iterator(); + return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); +} + +simdjson_inline simdjson_result 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 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; + } +} + +simdjson_inline simdjson_result> document::at_path_with_wildcard(std::string_view json_path) noexcept { + rewind(); // Rewind the document each time 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().at_path_with_wildcard(json_path); + case json_type::object: + return (*this).get_object().at_path_with_wildcard(json_path); + default: + return INVALID_JSON_POINTER; + } +} + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (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) { + // 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::simdjson_result( + rvv_vls::ondemand::document &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base( + error + ) +{ +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { + if (error()) { return error(); } + return first.get_value(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} + +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} + +template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; +template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first); +} +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(rvv_vls::ondemand::document &out) & noexcept = delete; +template<> simdjson_warn_unused simdjson_inline error_code simdjson_result::get(rvv_vls::ondemand::document &out) && noexcept { + if (error()) { return error(); } + out = std::forward(first); + return SUCCESS; +} + +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} + +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} + +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} + +simdjson_inline bool simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} + +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} + +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} + +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} + + +#if SIMDJSON_EXCEPTIONS +template ::value == false>::type> +simdjson_inline simdjson_result::operator T() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator rvv_vls::ondemand::array() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator rvv_vls::ondemand::object() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator rvv_vls::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator rvv_vls::ondemand::value() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline bool simdjson_result::at_end() const noexcept { + if (error()) { return error(); } + return first.at_end(); +} + + +simdjson_inline int32_t simdjson_result::current_depth() const noexcept { + if (error()) { return error(); } + return first.current_depth(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path(json_path); +} + +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path_with_wildcard(json_path); +} + +#if SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(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 document_reference::get_array() & noexcept { return doc->get_array(); } +simdjson_inline simdjson_result 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 document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } +simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } +simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } +template +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 document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } +simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } +simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } +simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } +simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline document_reference::operator T() noexcept(false) { return get(); } +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 document_reference::count_elements() & noexcept { return doc->count_elements(); } +simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } +simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } +simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } +simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } +simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } +simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } +simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } +simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } +simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } +simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } +simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } +simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } +simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } +simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } +simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } +simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } +simdjson_inline simdjson_result> document_reference::at_path_with_wildcard(std::string_view json_path) noexcept { return doc->at_path_with_wildcard(json_path); } +simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} +simdjson_inline document_reference::operator document&() const noexcept { return *doc; } +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code document_reference::extract_into(T& out) & noexcept { + return doc->extract_into(out); +} +#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION +} // namespace ondemand +} // namespace rvv_vls +} // namespace simdjson + + + +namespace simdjson { +simdjson_inline simdjson_result::simdjson_result(rvv_vls::ondemand::document_reference value, error_code error) + noexcept : implementation_simdjson_result_base(std::forward(value), error) {} + + +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { + if (error()) { return error(); } + return first.get_value(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} +template <> +simdjson_warn_unused simdjson_inline error_code simdjson_result::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::get(rvv_vls::ondemand::document_reference &out) && noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline simdjson_result::operator T() noexcept(false) { + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + if (error()) { throw simdjson_error(error()); } + return first.get(); +} +simdjson_inline simdjson_result::operator rvv_vls::ondemand::array() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator rvv_vls::ondemand::object() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator rvv_vls::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator rvv_vls::ondemand::value() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path_with_wildcard(json_path); +} +#if SIMDJSON_STATIC_REFLECTION +template + requires(std::is_class_v && (sizeof...(FieldNames) > 0)) +simdjson_warn_unused simdjson_inline error_code simdjson_result::extract_into(T& out) & noexcept { + if (error()) { return error(); } + return first.extract_into(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 +#include + +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 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 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 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 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 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 +) 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}, + 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}, + allow_comma_separated{false}, + 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 { + 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 document_stream::iterator::operator*() noexcept { + return simdjson_result(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; } + doc_index = batch_start; + 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 + 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(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; + } + } +} + +inline void document_stream::next_document() noexcept { + // 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(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; + if (remaining <= batch_size) { + return p.implementation->stage1(&buf[_batch_start], remaining, stage1_mode::streaming_final); + } else { + return p.implementation->stage1(&buf[_batch_start], batch_size, stage1_mode::streaming_partial); + } +} + +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 { + auto depth = stream->doc.iter.depth(); + auto cur_struct_index = stream->doc.iter._root - stream->parser->implementation->structural_indexes.get(); + + // If at root, process the first token to determine if scalar value + if (stream->doc.iter.at_root()) { + 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 + // TODO: We could remove trailing whitespaces + // This returns a string spanning from start of value to the beginning of the next document (excluded) + { + auto next_index = 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(stream->buf) + current_index(); + while(svlen > 1 && (std::isspace(start[svlen-1]) || start[svlen-1] == '\0')) { + svlen--; + } + return std::string_view(start, svlen); + } + } + cur_struct_index++; + } + + while (cur_struct_index <= static_cast(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(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::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base(error) +{ +} +simdjson_inline simdjson_result::simdjson_result( + rvv_vls::ondemand::document_stream &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(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() {} + +simdjson_inline field::field(raw_json_string key, ondemand::value &&value) noexcept + : std::pair(key, std::forward(value)) +{ +} + +simdjson_inline simdjson_result 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::start(const value_iterator &parent_iter, raw_json_string key) noexcept { + return field(key, parent_iter.child()); +} + +simdjson_inline simdjson_warn_unused simdjson_result 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 answer = first.unescape(second.iter.json_iter(), allow_replacement); + first.consume(); + return answer; +} + +template +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) ); + 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(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(first.buf), end_quote - first.buf); +} + +simdjson_inline value &field::value() & noexcept { + return second; +} + +simdjson_inline value field::value() && noexcept { + return std::forward(*this).second; +} + +} // namespace ondemand +} // namespace rvv_vls +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + rvv_vls::ondemand::field &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base(error) +{ +} + +simdjson_inline simdjson_result simdjson_result::key() noexcept { + if (error()) { return error(); } + return first.key(); +} + +simdjson_inline simdjson_result simdjson_result::key_raw_json_token() noexcept { + if (error()) { return error(); } + return first.key_raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::escaped_key() noexcept { + if (error()) { return error(); } + return first.escaped_key(); +} + +simdjson_inline simdjson_result simdjson_result::unescaped_key(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.unescaped_key(allow_replacement); +} + +template +simdjson_warn_unused simdjson_inline error_code simdjson_result::unescaped_key(string_type &receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.unescaped_key(receiver, allow_replacement); +} + +simdjson_inline simdjson_result simdjson_result::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(other.token)), + parser{other.parser}, + _string_buf_loc{other._string_buf_loc}, + error{other.error}, + _depth{other._depth}, + _root{other._root}, + _streaming{other._streaming} +{ + 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; + 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} + +{ + 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 '"': + if(*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(TAPE_ERROR, "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; +} + +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(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 json_iterator::current_location() const noexcept { + if (!is_alive()) { // Unrecoverable error + if (!at_root()) { + return reinterpret_cast(token.peek(-1)); + } else { + return reinterpret_cast(token.peek()); + } + } + if (at_end()) { + return OUT_OF_BOUNDS; + } + return reinterpret_cast(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 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 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::simdjson_result(rvv_vls::ondemand::json_iterator &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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; + default: SIMDJSON_UNREACHABLE(); + } + return out; +} + +#if SIMDJSON_EXCEPTIONS +inline std::ostream& operator<<(std::ostream& out, simdjson_result &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::simdjson_result(rvv_vls::ondemand::json_type &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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 +#include + +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 +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_s); + if (size <= 0) return std::string(); + std::unique_ptr 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 +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)...); +} + +template +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)...); + 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): #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 */ +/* amalgamation skipped (editor-only): #endif */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace rvv_vls { +namespace ondemand { + +simdjson_inline simdjson_result 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} +simdjson_inline simdjson_result 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} +simdjson_inline simdjson_result object::operator[](const std::string_view key) & noexcept { + return find_field_unordered(key); +} +simdjson_inline simdjson_result object::operator[](const std::string_view key) && noexcept { + return std::forward(*this).find_field_unordered(key); +} +simdjson_inline simdjson_result object::find_field(const std::string_view key) & noexcept { + 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} +simdjson_inline simdjson_result object::find_field(const std::string_view key) && noexcept { + 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(key.size()), key.data()); + return NO_SUCH_FIELD; + } + return value(iter.child()); +} + +simdjson_inline simdjson_result object::start(value_iterator &iter) noexcept { + SIMDJSON_TRY( iter.start_object().error() ); + return object(iter); +} +simdjson_inline simdjson_result 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 object::raw_json() noexcept { + 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(starting_point), size_t(final_point - starting_point)); +} + +simdjson_inline simdjson_result 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::begin() noexcept { +#if SIMDJSON_DEVELOPMENT_CHECKS + if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } +#endif + return object_iterator(iter); +} +simdjson_inline simdjson_result object::end() noexcept { + return object_iterator(iter); +} + +inline simdjson_result object::at_pointer(std::string_view json_pointer) noexcept { + if (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 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 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); +} + +inline simdjson_result> object::at_path_with_wildcard(std::string_view json_path) noexcept { + std::vector result; + + 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; + // 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()) { + result.push_back(std::move(val)); + } else { + auto nested_result = val.at_path_with_wildcard(remaining_path); + + if (nested_result.error()) { + return nested_result.error(); + } + // Extract and append all nested matches to our result + std::vector nested_vec; + SIMDJSON_TRY(std::move(nested_result).get(nested_vec)); + + result.insert(result.end(), + std::make_move_iterator(nested_vec.begin()), + std::make_move_iterator(nested_vec.end())); + } + } + return result; + } else { + value val; + SIMDJSON_TRY(find_field(key).get(val)); + + if (remaining_path.empty()) { + result.push_back(std::move(val)); + return result; + } else { + return val.at_path_with_wildcard(remaining_path); + } + } +} + +simdjson_inline simdjson_result 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 object::is_empty() & noexcept { + 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 object::reset() & noexcept { + return iter.reset_object(); +} + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION + +template + requires(std::is_class_v && (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) { + // 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::simdjson_result(rvv_vls::ondemand::object &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(error) {} + +simdjson_inline simdjson_result simdjson_result::begin() noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() noexcept { + if (error()) { return error(); } + return first.end(); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) && noexcept { + if (error()) { return error(); } + return std::forward(first).find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) && noexcept { + if (error()) { return error(); } + return std::forward(first)[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) && noexcept { + if (error()) { return error(); } + return std::forward(first).find_field(key); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + +simdjson_inline simdjson_result> simdjson_result::at_path_with_wildcard(std::string_view json_path) noexcept { + if (error()) { return error(); } + return first.at_path_with_wildcard(json_path); +} + +inline simdjson_result simdjson_result::reset() noexcept { + if (error()) { return error(); } + return first.reset(); +} + +inline simdjson_result simdjson_result::is_empty() noexcept { + if (error()) { return error(); } + return first.is_empty(); +} + +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} + +simdjson_inline simdjson_result simdjson_result::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} +{} + +simdjson_inline simdjson_result 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::simdjson_result( + rvv_vls::ondemand::object_iterator &&value +) noexcept + : implementation_simdjson_result_base(std::forward(value)) +{ + first.iter.assert_is_valid(); +} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base({}, error) +{ +} + +simdjson_inline simdjson_result simdjson_result::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::operator==(const simdjson_result &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::operator!=(const simdjson_result &other) const noexcept { + if (!first.iter.is_valid()) { return error(); } + return first != other.first; +} +// Checks for ']' and ',' +simdjson_inline simdjson_result &simdjson_result::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/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; + 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 parser::iterate(padded_string_view json) & noexcept { + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } + + json.remove_utf8_bom(); + + // 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(json.data()), json.length(), stage1_mode::regular) ); + return document::start({ reinterpret_cast(json.data()), this }); +} + +#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept { + if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; } + + json.remove_utf8_bom(); + + // 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(json.data()), json.length(), stage1_mode::regular); + if (err) { + if (err != UNCLOSED_STRING) + return err; + } + return document::start({ reinterpret_cast(json.data()), this, true }); +} +#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON + +simdjson_warn_unused simdjson_inline simdjson_result 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 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 parser::iterate(std::string_view json, size_t allocated) & noexcept { + return iterate(padded_string_view(json, allocated)); +} + +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(std::string &json) & noexcept { + return iterate(pad_with_reserve(json)); +} + +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const std::string &json) & noexcept { + return iterate(padded_string_view(json)); +} + +simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const simdjson_result &result) & noexcept { + // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 parser::iterate(const simdjson_result &result) & noexcept { + // We don't presently have a way to temporarily get a const T& from a simdjson_result 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 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(json.data()), json.length(), stage1_mode::regular) ); + return json_iterator(reinterpret_cast(json.data()), this); +} + +inline simdjson_result 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; + } + if(allow_comma_separated && batch_size < len) { batch_size = len; } + return document_stream(*this, buf, len, batch_size, allow_comma_separated); +} + +inline simdjson_result 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(buf), len, batch_size, allow_comma_separated); +} +inline simdjson_result 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 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 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 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_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 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(dst), end-dst); + dst = end; + return result; +} + +simdjson_inline simdjson_warn_unused simdjson_result 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(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 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& parser::get_parser_instance() { + std::unique_ptr& 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& 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 parser_instance = nullptr; + return parser_instance; +} + + +} // namespace ondemand +} // namespace rvv_vls +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result(rvv_vls::ondemand::parser &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(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(buf); +} + +simdjson_inline char raw_json_string::operator[](size_t i) const noexcept { + return reinterpret_cast(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()); + } + const char * r{raw()}; + size_t pos{0}; + for(;pos < target.size();pos++) { + if(r[pos] != target[pos]) { return false; } + } + if(r[pos] != '"') { return false; } + return true; +} + +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 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 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 rvv_vls +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result(rvv_vls::ondemand::raw_json_string &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(error) {} + +simdjson_inline simdjson_result simdjson_result::raw() const noexcept { + if (error()) { return error(); } + return first.raw(); +} +simdjson_inline char simdjson_result::operator[](size_t i) const noexcept { + if (error()) { return error(); } + return first[i]; +} +simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::unescape(rvv_vls::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 simdjson_result::unescape_wobbly(rvv_vls::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 rvv_vls */ +/* including simdjson/generic/ondemand/token_iterator-inl.h for rvv_vls: #include "simdjson/generic/ondemand/token_iterator-inl.h" */ +/* begin file simdjson/generic/ondemand/token_iterator-inl.h for rvv_vls */ +#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 rvv_vls { +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 rvv_vls +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result(rvv_vls::ondemand::token_iterator &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(error) {} + +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H +/* end file simdjson/generic/ondemand/token_iterator-inl.h for rvv_vls */ +/* including simdjson/generic/ondemand/value_iterator-inl.h for rvv_vls: #include "simdjson/generic/ondemand/value_iterator-inl.h" */ +/* begin file simdjson/generic/ondemand/value_iterator-inl.h for rvv_vls */ +#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 rvv_vls { +namespace ondemand { + +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 value_iterator::start_object() noexcept { + SIMDJSON_TRY( start_container('{', "Not an object", "object") ); + return started_object(); +} + +simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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 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 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_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 value_iterator::start_array() noexcept { + SIMDJSON_TRY( start_container('[', "Not an array", "array") ); + return started_array(); +} + +simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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(!not_true); +} +simdjson_warn_unused simdjson_inline simdjson_result 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 value_iterator::get_string(bool allow_replacement) noexcept { + return get_raw_json_string().unescape(json_iter(), allow_replacement); +} +template +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; } + 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 value_iterator::get_wobbly_string() noexcept { + return get_raw_json_string().unescape_wobbly(json_iter()); +} +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_raw_json_string() noexcept { + auto json = peek_scalar("string"); + if (*json != '"') { return incorrect_type_error("Not a string"); } + advance_scalar("string"); + return raw_json_string(json+1); +} +simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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 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 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 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 value_iterator::is_integer() noexcept { + return numberparsing::is_integer(peek_non_root_scalar("integer")); +} +simdjson_inline simdjson_result value_iterator::get_number_type() noexcept { + return numberparsing::get_number_type(peek_non_root_scalar("integer")); +} +simdjson_inline simdjson_result 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 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 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.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 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.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 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 +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; } + 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 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 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; } + advance_scalar("string"); + return raw_json_string(json+1); +} +simdjson_warn_unused simdjson_inline simdjson_result 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 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 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 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 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.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 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.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 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 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 simdjson_result value_iterator::reset_array() noexcept { + if(error()) { return error(); } + move_at_container_start(); + return started_array(); +} + +simdjson_inline simdjson_result 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 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 rvv_vls +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result(rvv_vls::ondemand::value_iterator &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(error) {} + +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H +/* end file simdjson/generic/ondemand/value_iterator-inl.h for rvv_vls */ +/* including simdjson/generic/ondemand/serialization-inl.h for rvv_vls: #include "simdjson/generic/ondemand/serialization-inl.h" */ +/* begin file simdjson/generic/ondemand/serialization-inl.h for rvv_vls */ +#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 to_json_string(rvv_vls::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 to_json_string(rvv_vls::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 to_json_string(rvv_vls::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 rvv_vls::ondemand; + rvv_vls::ondemand::json_type t; + auto error = x.type().get(t); + if(error != SUCCESS) { return error; } + switch (t) + { + case json_type::array: + { + rvv_vls::ondemand::array array; + error = x.get_array().get(array); + if(error) { return error; } + return to_json_string(array); + } + case json_type::object: + { + rvv_vls::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 to_json_string(rvv_vls::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 to_json_string(rvv_vls::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 to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); +} +} // namespace simdjson + +namespace simdjson { namespace rvv_vls { namespace ondemand { + +#if SIMDJSON_EXCEPTIONS +inline std::ostream& operator<<(std::ostream& out, simdjson::rvv_vls::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 x) { + if (x.error()) { throw simdjson::simdjson_error(x.error()); } + return (out << x.value()); +} +#else +inline std::ostream& operator<<(std::ostream& out, simdjson::rvv_vls::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::rvv_vls::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 x) { + if (x.error()) { throw simdjson::simdjson_error(x.error()); } + return (out << x.value()); +} +#else +inline std::ostream& operator<<(std::ostream& out, simdjson::rvv_vls::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::rvv_vls::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::rvv_vls::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&& x) { + if (x.error()) { throw simdjson::simdjson_error(x.error()); } + return (out << x.value()); +} +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& x) { + if (x.error()) { throw simdjson::simdjson_error(x.error()); } + return (out << x.value()); +} +#else +inline std::ostream& operator<<(std::ostream& out, simdjson::rvv_vls::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::rvv_vls::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 x) { + if (x.error()) { throw simdjson::simdjson_error(x.error()); } + return (out << x.value()); +} +#else +inline std::ostream& operator<<(std::ostream& out, simdjson::rvv_vls::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::rvv_vls::ondemand + +#endif // SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H +/* end file simdjson/generic/ondemand/serialization-inl.h for rvv_vls */ + +// JSON path accessor (compile-time) - must be after inline definitions +/* including simdjson/generic/ondemand/compile_time_accessors.h for rvv_vls: #include "simdjson/generic/ondemand/compile_time_accessors.h" */ +/* begin file simdjson/generic/ondemand/compile_time_accessors.h for rvv_vls */ +/** + * 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 emails; }; + * + * std::string email; + * path_accessor::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 +#include +#include + +namespace simdjson { +namespace rvv_vls { +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::rvv_vls::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 +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 +consteval auto make_field_step(const char (&name)[N]) { + return path_step(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 +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 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 +struct path_accessor { + using value = ::simdjson::rvv_vls::ondemand::value; + + static constexpr auto parser = json_path_parser(); + 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 + static inline simdjson_result access(DocOrValue& doc_or_val) noexcept { + // Validate path at compile time if T is a struct + if constexpr (std::is_class_v) { + 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(doc_or_val.get_value()); + } + + // Extract value at path directly into target with compile-time type validation + // Example: std::string name; path_accessor::extract_field(doc, name); + template + static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept { + static_assert(std::is_class_v, "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(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 + static consteval std::enable_if_t, 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 + static inline error_code extract_with_reflection(simdjson_result 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(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(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_value, target_ref); + } + } + } + // Skip unexpected characters and continue + else { + return extract_with_reflection(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 + static inline simdjson_result access_impl(simdjson_result 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_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_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_value); + } + } else { + return access_impl(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(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 + 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 + 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 + static consteval bool is_container_type() { + using BaseType = std::remove_cvref_t; + if constexpr (requires { typename BaseType::value_type; }) { + return true; + } + if constexpr (std::is_array_v) { + return true; + } + return false; + } + + // Extract element type from non-reflected container + template + using extract_element_type = std::conditional_t< + requires { typename std::remove_cvref_t::value_type; }, + typename std::remove_cvref_t::value_type, + std::conditional_t< + std::is_array_v>, + std::remove_extent_t>, + void + > + >; + + // Validate path matches struct definition + static consteval bool validate_path() { + if constexpr (!std::is_class_v) { + 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 +inline simdjson_result<::simdjson::rvv_vls::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = path_accessor; + return accessor::access(doc_or_val); +} + +// Overload without type parameter (no validation) +template +inline simdjson_result<::simdjson::rvv_vls::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = path_accessor; + return accessor::access(doc_or_val); +} + +// ============================================================================ +// JSON Pointer Compile-Time Support (RFC 6901) +// ============================================================================ + +// JSON Pointer parser: /field/0/nested (slash-separated) +template +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 +struct pointer_accessor { + using parser = json_pointer_parser; + 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) { + 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::is_array_like_reflected(current_type)) { + return false; + } + current_type = path_accessor::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 + static inline simdjson_result access_impl(simdjson_result 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(next_value); + } else { + auto obj = current.get_object().value_unsafe(); + auto next_value = obj.find_field_unordered(token); + return access_impl(next_value); + } + } + } + + // Access JSON value at pointer + template + static inline simdjson_result access(DocOrValue& doc_or_val) noexcept { + if constexpr (std::is_class_v) { + 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 current = doc_or_val.get_value(); + return access_impl<0>(current); + } + + // Extract value at pointer directly into target with type validation + template + static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept { + static_assert(std::is_class_v, "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 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 + static consteval std::enable_if_t, 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::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 +inline simdjson_result<::simdjson::rvv_vls::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = pointer_accessor; + return accessor::access(doc_or_val); +} + +// Overload without type parameter (no validation) +template +inline simdjson_result<::simdjson::rvv_vls::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept { + using accessor = pointer_accessor; + return accessor::access(doc_or_val); +} + +} // namespace json_path +} // namespace ondemand +} // namespace rvv_vls +} // 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 rvv_vls */ + +/* end file simdjson/generic/ondemand/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_ONDEMAND_H +/* end file simdjson/rvv-vls/ondemand.h */ #else #error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION #endif @@ -118839,24 +182319,1525 @@ namespace simdjson { #undef SIMDJSON_CONDITIONAL_INCLUDE namespace simdjson { - /** - * @copydoc simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand - */ - namespace ondemand = SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand; + /** + * @copydoc simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand + */ + namespace ondemand = SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand; } // namespace simdjson #endif // SIMDJSON_BUILTIN_ONDEMAND_H /* end file simdjson/builtin/ondemand.h */ namespace simdjson { - /** - * @copydoc simdjson::builtin::ondemand - */ - namespace ondemand = builtin::ondemand; + /** + * @copydoc simdjson::builtin::ondemand + */ + namespace ondemand = builtin::ondemand; + /** + * @copydoc simdjson::builtin::builder + */ + namespace builder = builtin::builder; + +#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 + requires(!std::same_as && + !std::same_as && + !std::same_as && + !std::same_as) + inline std::string to_json_string(const T& obj) { + builder::string_builder str_builder; + append(str_builder, obj); + std::string_view view; + if (str_builder.view().get(view) == SUCCESS) { + return std::string(view); + } + return ""; + } +#endif + } // namespace simdjson #endif // SIMDJSON_ONDEMAND_H /* end file simdjson/ondemand.h */ +/* including simdjson/convert.h: #include "simdjson/convert.h" */ +/* begin file simdjson/convert.h */ + +#ifndef SIMDJSON_CONVERT_H +#define SIMDJSON_CONVERT_H + +/* skipped duplicate #include "simdjson/ondemand.h" */ +#include + +#if SIMDJSON_SUPPORTS_CONCEPTS + + +namespace simdjson { +namespace convert { +namespace internal { + +/** + * A utility class for automatically parsing JSON documents. + * This template is NOT part of our public API. + * It is subject to changes. + * @private + */ +template +struct auto_parser { +private: + parser_type m_parser; + ondemand::document m_doc; + error_code m_error{SUCCESS}; + + template + static constexpr bool is_nothrow_gettable = requires(ondemand::document doc) { + { doc.get() } noexcept; + }; +public: + explicit auto_parser(parser_type &&parser, ondemand::document &&doc) noexcept requires(!std::is_pointer_v); + explicit auto_parser(parser_type &&parser, padded_string_view const str) noexcept requires(!std::is_pointer_v); + explicit auto_parser(std::remove_pointer_t &parser, ondemand::document &&doc) noexcept requires(std::is_pointer_v); + explicit auto_parser(std::remove_pointer_t &parser, padded_string_view const str) noexcept requires(std::is_pointer_v); + explicit auto_parser(padded_string_view const str) noexcept requires(std::is_pointer_v); + explicit auto_parser(parser_type parser, ondemand::document &&doc) noexcept requires(std::is_pointer_v); + auto_parser(auto_parser const &) = delete; + auto_parser &operator=(auto_parser const &) = delete; + auto_parser(auto_parser &&) noexcept = default; + auto_parser &operator=(auto_parser &&) noexcept = default; + ~auto_parser() = default; + + simdjson_warn_unused std::remove_pointer_t &parser() noexcept; + + template + simdjson_warn_unused simdjson_inline simdjson_result result() noexcept(is_nothrow_gettable); + template + simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept(is_nothrow_gettable); + + + simdjson_warn_unused simdjson_inline simdjson_result array() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result object() noexcept; + simdjson_warn_unused simdjson_inline simdjson_result number() noexcept; + + +#if SIMDJSON_EXCEPTIONS + template + simdjson_warn_unused simdjson_inline explicit(false) operator T() noexcept(false); +#endif // SIMDJSON_EXCEPTIONS + + template + simdjson_warn_unused simdjson_inline std::optional optional() noexcept(is_nothrow_gettable); +}; + + +/** + * A utility class for adapting values for the `auto_parser`. + * This template is not part of our public API. It is subject to changes. + * @private + */ +template +struct to_adaptor { + T operator()(simdjson_result &val) const noexcept; + auto operator()(padded_string_view const str) const noexcept; + auto operator()(ondemand::parser &parser, padded_string_view const str) const noexcept; + // The std::string is padded with reserve to ensure there is enough space for padding. + // Some sanitizers may not like this, so you can use simdjson::pad instead. + // simdjson::from(simdjson::pad(str)) + auto operator()(std::string str) const noexcept; + auto operator()(ondemand::parser &parser, std::string str) const noexcept; +}; +// deduction guide +auto_parser(padded_string_view const str) -> auto_parser; +} // namespace internal +} // namespace convert + +/** + * The simdjson::from instance is EXPERIMENTAL AND SUBJECT TO CHANGES. + * + * The `from` instance is a utility adaptor for parsing JSON strings into objects. + * It provides a convenient way to convert JSON data into C++ objects using the `auto_parser`. + * + * Example usage: + * + * ```cpp + * std::map obj = + * simdjson::from(R"({"key": "value"})"_padded); + * ``` + * + * This will parse the JSON string and return an object representation. By default, we + * use the simdjson::ondemand::parser::get_parser() instance. A parser instance should + * be used for just one document at a time. + * + * You can also pass you own parser instance: + * ```cpp + * simdjson::ondemand::parser parser; + * std::map obj = + * simdjson::from(parser, R"({"key": "value"})"_padded); + * ``` + * The parser instance can be reused. + * + * This functionality requires C++20 or better. + */ +static constexpr convert::internal::to_adaptor<> from{}; + +} // namespace simdjson + +#endif // SIMDJSON_SUPPORTS_CONCEPTS +#endif // SIMDJSON_CONVERT_H +/* end file simdjson/convert.h */ +/* including simdjson/convert-inl.h: #include "simdjson/convert-inl.h" */ +/* begin file simdjson/convert-inl.h */ + +#ifndef SIMDJSON_CONVERT_INL_H +#define SIMDJSON_CONVERT_INL_H + +/* skipped duplicate #include "simdjson/convert.h" */ +#if SIMDJSON_SUPPORTS_CONCEPTS +namespace simdjson { +namespace convert { +namespace internal { +// auto_parser method definitions +template +inline auto_parser::auto_parser(parser_type &&parser, ondemand::document &&doc) noexcept requires(!std::is_pointer_v) + : m_parser{std::move(parser)}, m_doc{std::move(doc)} {} + +template +inline auto_parser::auto_parser(parser_type &&parser, padded_string_view const str) noexcept requires(!std::is_pointer_v) + : m_parser{std::move(parser)}, m_doc{}, m_error{SUCCESS} { + m_error = m_parser.iterate(str).get(m_doc); +} + +template +inline auto_parser::auto_parser(std::remove_pointer_t &parser, ondemand::document &&doc) noexcept requires(std::is_pointer_v) + : m_parser{&parser}, m_doc{std::move(doc)} {} + +template +inline auto_parser::auto_parser(std::remove_pointer_t &parser, padded_string_view const str) noexcept requires(std::is_pointer_v) + : m_parser{&parser}, m_doc{}, m_error{SUCCESS} { + m_error = m_parser->iterate(str).get(m_doc); +} + +template +inline auto_parser::auto_parser(padded_string_view const str) noexcept requires(std::is_pointer_v) + : auto_parser{ondemand::parser::get_parser(), str} {} + +template +inline auto_parser::auto_parser(parser_type parser, ondemand::document &&doc) noexcept requires(std::is_pointer_v) + : auto_parser{*parser, std::move(doc)} {} + + + + + +template +inline std::remove_pointer_t &auto_parser::parser() noexcept { + if constexpr (std::is_pointer_v) { + return *m_parser; + } else { + return m_parser; + } +} + +template +template +inline simdjson_result auto_parser::result() noexcept(is_nothrow_gettable) { + if (m_error != SUCCESS) { + return m_error; + } + return m_doc.get(); +} + +template +template +simdjson_warn_unused simdjson_inline error_code auto_parser::get(T &value) && noexcept(is_nothrow_gettable) { + return result().get(value); +} + +template +inline simdjson_result auto_parser::array() noexcept { + return result(); +} + +template +inline simdjson_result auto_parser::object() noexcept { + return result(); +} + +template +inline simdjson_result auto_parser::number() noexcept { + return result(); +} + +#if SIMDJSON_EXCEPTIONS +template +template +inline auto_parser::operator T() noexcept(false) { + if (m_error != SUCCESS) { + throw simdjson_error(m_error); + } + return m_doc.get(); +} +#endif // SIMDJSON_EXCEPTIONS + +template +template +inline std::optional auto_parser::optional() noexcept(is_nothrow_gettable) { + if (m_error != SUCCESS) { + return std::nullopt; + } + T value; + if (m_doc.get().get(value)) [[unlikely]] { + return std::nullopt; + } + return {std::move(value)}; +} + +// to_adaptor method definitions +template +inline T to_adaptor::operator()(simdjson_result &val) const noexcept { + return val.get(); +} + +template +inline auto to_adaptor::operator()(padded_string_view const str) const noexcept { + return auto_parser{str}; +} + +template +inline auto to_adaptor::operator()(ondemand::parser &parser, padded_string_view const str) const noexcept { + return auto_parser{parser, str}; +} + +template +inline auto to_adaptor::operator()(std::string str) const noexcept { + return auto_parser{pad_with_reserve(str)}; +} + +template +inline auto to_adaptor::operator()(ondemand::parser &parser, std::string str) const noexcept { + return auto_parser{parser, pad_with_reserve(str)}; +} +} // namespace internal +} // namespace convert +} // namespace simdjson +#endif // SIMDJSON_SUPPORTS_CONCEPTS +#endif // SIMDJSON_CONVERT_INL_H +/* end file simdjson/convert-inl.h */ + +// Compile-time JSON parsing (C++26 P2996 reflection) +/* including simdjson/compile_time_json.h: #include "simdjson/compile_time_json.h" */ +/* begin file simdjson/compile_time_json.h */ +/** + * @file compile_time_json.h + * @brief Compile-time JSON parsing using C++26 reflection with + * std::meta::substitute() + */ + +#ifndef SIMDJSON_GENERIC_COMPILE_TIME_JSON_H +#define SIMDJSON_GENERIC_COMPILE_TIME_JSON_H + +#if SIMDJSON_STATIC_REFLECTION + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace simdjson { +namespace compile_time { + +/** + * @brief Compile-time JSON parser. This function parses the provided JSON + * string at compile time and returns a custom struct type representing the JSON + * object. + * + * We have a few limitations which trigger compile-time errors if violated: + * - Only JSON objects and arrays are supported at the top level (no primitives). + * We will lift this limitation in the future. + * - Strings are represented using the const char * in UTF-8, but they must not + * contain embedded nulls. We would prefer to represent them as std::string or + * std::string_view, and hope to do so in the future. + * - Heterogeneous arrays are not supported yet. E.g., you need to have arrays of + * all integers, or all strings, all floats, all compatible objects, etc. + * For example, the following is accepted: + * [ + * { "name": "Alice", "age": 30 }, + * { "name": "Bob", "age": 25 }, + * { "name": "Charlie", "age": 35 } + * ] + * but the following is not: + * [ + * { "name": "Alice", "age": 30 }, + * "Just a string", + * 42, + * { "name": "Charlie", "age": 35 } + * ] + * + * We may support heterogeneous arrays in the future with std::variant types. + * - We parse the first JSON document encountered in the string. Trailing + * characters are ignored. Thus if your JSON begins with {"a":1}, everything + * after the closing } is ignored. This limitation will be lifted in the future, + * reporting an error. + * + * These limitations are safe in the sense that they result in compile-time errors. + * Thus you will not get truncated strings or imprecise floats silently. + * + * This function is subject to change in the future. + */ +template consteval auto parse_json(); + +} // namespace compile_time +} // namespace simdjson + + +template +consteval auto operator ""_json() { + return simdjson::compile_time::parse_json(); +} + +#endif // SIMDJSON_STATIC_REFLECTION +#endif // SIMDJSON_GENERIC_COMPILE_TIME_JSON_H +/* end file simdjson/compile_time_json.h */ +/* including simdjson/compile_time_json-inl.h: #include "simdjson/compile_time_json-inl.h" */ +/* begin file simdjson/compile_time_json-inl.h */ +/** + * @file compile_time_json-inl.h + * @brief Implementation details for compile-time JSON parsing + * + * This file contains inline implementations and helper utilities for + * compile-time JSON parsing. Currently, the main implementation is + * self-contained in the header. + */ + +#ifndef SIMDJSON_GENERIC_COMPILE_TIME_JSON_INL_H + +#if SIMDJSON_STATIC_REFLECTION + +/* skipped duplicate #include "simdjson/compile_time_json.h" */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define simdjson_consteval_error(...) \ + { \ + std::abort(); \ + } + +namespace simdjson { +namespace compile_time { + +/** + * Namespace for number parsing utilities. + * We seek to provide exact compile-time number parsing functions. + * That is not trivial, but thankfully we can reuse much of the existing + * simdjson functionality. + * Importantly, it is not a trivial matter to provide correct rounding + * for floating-point numbers at compile-time. The fast_float library + * does it well. + */ +namespace number_parsing { + +// Counts the number of leading zeros in a 64-bit integer. +consteval int leading_zeroes(uint64_t input_num, int last_bit = 0) { + if (input_num & uint64_t(0xffffffff00000000)) { + input_num >>= 32; + last_bit |= 32; + } + if (input_num & uint64_t(0xffff0000)) { + input_num >>= 16; + last_bit |= 16; + } + if (input_num & uint64_t(0xff00)) { + input_num >>= 8; + last_bit |= 8; + } + if (input_num & uint64_t(0xf0)) { + input_num >>= 4; + last_bit |= 4; + } + if (input_num & uint64_t(0xc)) { + input_num >>= 2; + last_bit |= 2; + } + if (input_num & uint64_t(0x2)) { /* input_num >>= 1; */ + last_bit |= 1; + } + return 63 - last_bit; +} +// Multiplies two 32-bit unsigned integers and returns a 64-bit result. +consteval uint64_t emulu(uint32_t x, uint32_t y) { return x * (uint64_t)y; } +consteval uint64_t umul128_generic(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 = (uint64_t)(adbc < ad); + uint64_t lo = bd + (adbc << 32); + *hi = emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) + + (adbc_carry << 32) + (uint64_t)(lo < bd); + return lo; +} + +// Represents a 128-bit unsigned integer as two 64-bit parts. +// We have a value128 struct elsewhere in the simdjson, but we +// use a separate one here for clarity. +struct value128 { + uint64_t low; + uint64_t high; + + constexpr value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {} + constexpr value128() : low(0), high(0) {} +}; + +// Multiplies two 64-bit integers and returns a 128-bit result as value128. +consteval value128 full_multiplication(uint64_t a, uint64_t b) { + value128 answer; + answer.low = umul128_generic(a, b, &answer.high); + return answer; +} + +// Converts mantissa and exponent to a double, considering the sign. +consteval double to_double(uint64_t mantissa, int64_t exponent, bool negative) { + uint64_t sign_bit = negative ? (1ULL << 63) : 0; + uint64_t exponent_bits = (uint64_t(exponent) & 0x7FF) << 52; + uint64_t bits = sign_bit | exponent_bits | (mantissa & ((1ULL << 52) - 1)); + return std::bit_cast(bits); +} + +// Attempts to compute i * 10^(power) exactly; and if "negative" is +// true, negate the result. +// Returns true on success, false on failure. +// Failure suggests and invalid input or out-of-range result. +consteval bool compute_float_64(int64_t power, uint64_t i, bool negative, + double &d) { + if (i == 0) { + d = negative ? -0.0 : 0.0; + return true; + } + int64_t exponent = (((152170 + 65536) * power) >> 16) + 1024 + 63; + int lz = leading_zeroes(i); + i <<= lz; + const uint32_t index = + 2 * uint32_t(power - simdjson::internal::smallest_power); + value128 firstproduct = full_multiplication( + i, simdjson::internal::powers_template<>::power_of_five_128[index]); + if ((firstproduct.high & 0x1FF) == 0x1FF) { + value128 secondproduct = full_multiplication( + i, simdjson::internal::powers_template<>::power_of_five_128[index + 1]); + firstproduct.low += secondproduct.high; + if (secondproduct.high > firstproduct.low) { + firstproduct.high++; + } + } + uint64_t lower = firstproduct.low; + uint64_t upper = firstproduct.high; + uint64_t upperbit = upper >> 63; + uint64_t mantissa = upper >> (upperbit + 9); + lz += int(1 ^ upperbit); + int64_t real_exponent = exponent - lz; + if (real_exponent <= 0) { + if (-real_exponent + 1 >= 64) { + d = negative ? -0.0 : 0.0; + return true; + } + mantissa >>= -real_exponent + 1; + mantissa += (mantissa & 1); + mantissa >>= 1; + real_exponent = (mantissa < (uint64_t(1) << 52)) ? 0 : 1; + d = to_double(mantissa, real_exponent, negative); + return true; + } + if ((lower <= 1) && (power >= -4) && (power <= 23) && ((mantissa & 3) == 1)) { + if ((mantissa << (upperbit + 64 - 53 - 2)) == upper) { + mantissa &= ~1; + } + } + mantissa += mantissa & 1; + mantissa >>= 1; + if (mantissa >= (1ULL << 53)) { + mantissa = (1ULL << 52); + real_exponent++; + } + mantissa &= ~(1ULL << 52); + if (real_exponent > 2046) { + return false; + } + d = to_double(mantissa, real_exponent, negative); + return true; +} + +// Parses a single digit character and updates the integer value. +consteval bool parse_digit(const char c, uint64_t &i) { + const uint8_t digit = static_cast(c - '0'); + if (digit > 9) { + return false; + } + i = 10 * i + digit; + return true; +} + +// Parses a JSON float from a string starting at src. +// Returns the parsed double and the number of characters consumed. +consteval std::pair parse_double(const char *src, + const char *end) { + auto get_value = [&](const char *pointer) -> char { + if (pointer == end) { + return '\0'; + } + return *pointer; + }; + const char *srcinit = src; + bool negative = (get_value(src) == '-'); + src += uint8_t(negative); + uint64_t i = 0; + const char *p = src; + p += parse_digit(get_value(p), i); + bool leading_zero = (i == 0); + while (parse_digit(get_value(p), i)) { + p++; + } + if (p == src) { + simdjson_consteval_error("Invalid float value"); + } + if ((leading_zero && p != src + 1)) { + simdjson_consteval_error("Invalid float value"); + } + int64_t exponent = 0; + bool overflow; + if (get_value(p) == '.') { + p++; + const char *start_decimal_digits = p; + if (!parse_digit(get_value(p), i)) { + simdjson_consteval_error("Invalid float value"); + } // no decimal digits + p++; + while (parse_digit(get_value(p), i)) { + p++; + } + exponent = -(p - start_decimal_digits); + overflow = p - src - 1 > 19; + if (overflow && leading_zero) { + const char *start_digits = src + 2; + while (get_value(start_digits) == '0') { + start_digits++; + } + overflow = p - start_digits > 19; + } + } else { + overflow = p - src > 19; + } + if (overflow) { + simdjson_consteval_error( + "Overflow while computing the float value: too many digits"); + } + if (get_value(p) == 'e' || get_value(p) == 'E') { + p++; + bool exp_neg = get_value(p) == '-'; + p += exp_neg || get_value(p) == '+'; + uint64_t exp = 0; + const char *start_exp_digits = p; + while (parse_digit(get_value(p), exp)) { + p++; + } + if (p - start_exp_digits == 0 || p - start_exp_digits > 19) { + simdjson_consteval_error("Invalid float value"); + } + exponent += exp_neg ? 0 - exp : exp; + } + + overflow = overflow || exponent < simdjson::internal::smallest_power || + exponent > simdjson::internal::largest_power; + if (overflow) { + simdjson_consteval_error("Overflow while computing the float value"); + } + double d; + if (!compute_float_64(exponent, i, negative, d)) { + simdjson_consteval_error("Overflow while computing the float value"); + } + return {d, size_t(p - srcinit)}; +} +} // namespace number_parsing + +// JSON string may contain embedded nulls, and C++26 reflection does not yet +// support std::string_view as a data member type. As a workaround, we define +// a custom type that holds a const char* and a size. +template struct fixed_json_string { + // Statically-allocated array to hold the characters and a null terminator + static constexpr char inner_data[] = {Vals..., '\0'}; + + // Constant for the length of the string view (excluding the null terminator) + static constexpr std::size_t inner_size = sizeof...(Vals); + + // The std::string_view over the data. + // We use data and size to avoid including the null terminator in the view. + static constexpr std::string_view view = {inner_data, inner_size}; + + constexpr operator std::string_view() { return view; } + constexpr const char *c_str() { return view.data(); } + constexpr const char *data() { return view.data(); } + constexpr size_t size() { return view.size(); } +}; + +consteval std::meta::info to_fixed_json_string(std::string_view in) { + std::vector Args = {}; + for (char c : in) { + Args.push_back(std::meta::reflect_constant(c)); + } + return std::meta::substitute(^^fixed_json_string, Args); +} + +/** + * @brief Helper struct for substitute() pattern + */ +template struct type_builder { + struct constructed_type; + consteval { + std::meta::define_aggregate(^^constructed_type, { + meta_info...}); + } +}; + +/** + * @brief Type alias for the generated struct + * Usage: + * using struct = class_type; + */ +template +using class_type = type_builder::constructed_type; + +/** + */ + +/** + * @brief Variable template for constructing instances with values + */ +template constexpr auto construct_from = T{Vs...}; + +// in JSON, there are only a few whitespace characters that are allowed +// outside of objects, arrays, strings, and numbers. +[[nodiscard]] constexpr bool is_whitespace(char c) { + return c == ' ' || c == '\n' || c == '\t' || c == '\r'; +}; + +[[nodiscard]] constexpr std::string_view trim_whitespace(std::string_view str) { + size_t start = 0; + size_t end = str.size(); + + while (start < end && is_whitespace(str[start])) { + start++; + } + while (end > start && is_whitespace(str[end - 1])) { + end--; + } + return str.substr(start, end - start); +} + +// Forward declaration +consteval std::pair +parse_json_object_impl(std::string_view json); + +/////////////////////////////////////////////////// +/// NUMBER PARSING +/////////////////////////////////////////////////// + +// Parses a JSON number from a string view. +// Returns the number of characters consumed and the parsed value +// as a variant of int64_t, uint64_t, or double. +[[nodiscard]] consteval size_t +parse_number(std::string_view json, + std::variant &out) { + if (json.empty()) { + simdjson_consteval_error("Empty string is not a valid JSON number"); + } + if (json[0] == '+') { + simdjson_consteval_error("Invalid number: leading '+' sign is not allowed"); + } + // Compute the range and determine the type. + auto it = json.begin(); + if (it != json.end() && (*it == '-')) { + it++; + } + while (it != json.end() && (*it >= '0' && *it <= '9')) { + it++; + } + bool is_float = false; + if (it != json.end() && (*it == '.' || *it == 'e' || *it == 'E')) { + is_float = true; + if (*it == '.') { + it++; + while (it != json.end() && (*it >= '0' && *it <= '9')) { + it++; + } + } + if (it != json.end() && (*it == 'e' || *it == 'E')) { + it++; + if (it != json.end() && (*it == '+' || *it == '-')) { + it++; + } + while (it != json.end() && (*it >= '0' && *it <= '9')) { + it++; + } + } + } + size_t scope = it - json.begin(); + + bool is_negative = json[0] == '-'; + // Note that we consider -0 to be an integer unless it has a decimal point or + // exponent. + if (is_float) { + // It would be cool to use std::from_chars in a consteval context, but it is + // not supported yet for floating point types. :-( + auto [value, offset] = + number_parsing::parse_double(json.data(), json.data() + json.size()); + if (offset != scope) { + simdjson_consteval_error( + "Internal error: cannot agree on the character range of the float"); + } + out = value; + return offset; + } else if (is_negative) { + int64_t int_value = 0; + std::from_chars_result res = + std::from_chars(json.data(), json.data() + json.size(), int_value); + if (res.ec == std::errc()) { + out = int_value; + if ((res.ptr - json.data()) != scope) { + simdjson_consteval_error( + "Internal error: cannot agree on the character range of the float"); + } + return (res.ptr - json.data()); + } else { + simdjson_consteval_error("Invalid integer value"); + } + } else { + uint64_t uint_value = 0; + std::from_chars_result res = + std::from_chars(json.data(), json.data() + json.size(), uint_value); + if (res.ec == std::errc()) { + out = uint_value; + if ((res.ptr - json.data()) != scope) { + simdjson_consteval_error( + "Internal error: cannot agree on the character range of the float"); + } + return (res.ptr - json.data()); + } else { + simdjson_consteval_error("Invalid unsigned integer value"); + } + } +} + +//////////////////////////////////////////////////////// +/// STRING PARSING +//////////////////////////////////////////////////////// + +// parse a JSON string value, handling escape sequences and validating UTF-8 +// Returns the created string and the number of characters consumed. +// Note that the number of characters consumed includes the surrounding quotes. +// The number of bytes written to out differs from the number of characters +// consumed in general because of escape sequences and UTF-8 encoding. +[[nodiscard]] consteval std::pair +parse_string(std::string_view json) { + auto cursor = json.begin(); + auto end = json.end(); + std::string out; + + // Expect starting quote + if (cursor == end || *(cursor++) != '"') { + simdjson_consteval_error("Expected opening quote for string"); + } + // Notice that the quote is not appended. + + // Process \uXXXX escape sequences, writes out to out. + // Returns true on success, false on error. + auto process_escaped_unicode = [&] [[nodiscard]] () -> bool { + // Processing \uXXXX escape sequence is a bit complicated, so we + // define helper lambdas here. + // + // consume the XXXX in \uXXXX and return the corresponding code point. + // In case of error, a value greater than 0xFFFF is returned. + // The caller should check! + auto hex_to_u32 = [&] [[nodiscard]] () -> uint32_t { + auto digit = [](uint8_t c) -> uint32_t { + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + return 0xFFFFFFFF; + }; + if (end - cursor < 4) { + return 0xFFFFFFFF; + } + uint32_t d0 = digit(*cursor++); + uint32_t d1 = digit(*cursor++); + uint32_t d2 = digit(*cursor++); + uint32_t d3 = digit(*cursor++); + + return (d0 << 12) | (d1 << 8) | (d2 << 4) | d3; + }; + // Write code point as UTF-8 into out. + // The caller must ensure that the code point is valid, + // i.e., not in the surrogate range or greater than 0x10FFFF. + auto codepoint_to_utf8 = [&out] [[nodiscard]] (uint32_t cp) -> bool { + // the high and low surrogates U+D800 through U+DFFF are invalid code + // points + if (cp > 0x10FFFF || (cp >= 0xD800 && cp <= 0xDFFF)) { + return false; + } + if (cp <= 0x7F) { + out.push_back(uint8_t(cp)); + } else if (cp <= 0x7FF) { + out.push_back(uint8_t((cp >> 6) + 192)); + out.push_back(uint8_t((cp & 63) + 128)); + } else if (cp <= 0xFFFF) { + out.push_back(uint8_t((cp >> 12) + 224)); + out.push_back(uint8_t(((cp >> 6) & 63) + 128)); + out.push_back(uint8_t((cp & 63) + 128)); + } else if (cp <= 0x10FFFF) { + out.push_back(uint8_t((cp >> 18) + 240)); + out.push_back(uint8_t(((cp >> 12) & 63) + 128)); + out.push_back(uint8_t(((cp >> 6) & 63) + 128)); + out.push_back(uint8_t((cp & 63) + 128)); + } else { + return false; + } + return true; + }; + // We begin the implementation of process_escaped_unicode here. + // The substitution_code_point is used for invalid sequences. + // (This is U+FFFD, the Unicode replacement character.) + constexpr uint32_t substitution_code_point = 0xfffd; + // We need at least 4 characters for the hex digits + if (end - cursor < 4) { + return false; + } + // Consume the 4 hex digits + uint32_t code_point = hex_to_u32(); + // Check for validity + if (code_point > 0xFFFF) { + return false; + } + // If we have a high surrogate, we need to process a low surrogate + if (code_point >= 0xd800 && code_point < 0xdc00) { + // We need at least 6 more characters for \uXXXX, if they are NOT + // present, we have an error (isolated high surrogate), which we + // tolerate by substituting the substitution_code_point. + if (end - cursor < 6 || *cursor != '\\' || + *(cursor + 1) != 'u' > 0xFFFF) { + code_point = substitution_code_point; + } else { // we have \u following the high surrogate + cursor += 2; // skip \u + uint32_t code_point_2 = hex_to_u32(); + if (code_point_2 > 0xFFFF) { + return false; + } + uint32_t low_bit = code_point_2 - 0xdc00; + if (low_bit >> 10) { + // Not a valid low surrogate + code_point = substitution_code_point; + } else { + code_point = ((code_point - 0xd800) << 10 | low_bit) + 0x10000; + } + } + } else if (code_point >= 0xdc00 && code_point < 0xe000) { + // Isolated low surrogate, invalid + code_point = substitution_code_point; + } + // Now we have the final code point, write it as UTF-8 to out. + return codepoint_to_utf8(code_point); + }; + + while (cursor != end && *cursor != '"') { + // If we find the end of input before closing quote, it's an error + if (cursor == end) { + simdjson_consteval_error("Unterminated string"); + } + // capture the next character and move forward + char c = *(cursor++); + // The one case where we don't want to append to the string directly + // is when the string contains escape sequences. + + if (c == '\\') { + // Assume that it is the start of an escape sequence + size_t how_many_backslashes = 1; + while (cursor != end && *(cursor) == '\\') { + cursor++; + how_many_backslashes++; + } + size_t backslashes_to_add = how_many_backslashes / 2; + // Append the backslashes + for (size_t i = 0; i < backslashes_to_add; i++) { + out += '\\'; + } + // If we have an odd number of backslashes, we have an escape sequence + // besides the backslashes we have already added. + if (how_many_backslashes % 2 == 1) { + // If we have an odd number of backslashes, we must be in an escape + // sequence check that what follows is a valid escape character + if (cursor == end) { + simdjson_consteval_error("Truncated escape sequence in string"); + } + char next_char = *cursor; + cursor++; + switch (next_char) { + case '"': + out += '"'; + break; + case '/': + out += '/'; + break; + case 'b': + out += '\b'; + break; + case 'f': + out += '\f'; + break; + case 'n': + out += '\n'; + break; + case 'r': + out += '\r'; + break; + case 't': + out += '\t'; + break; + case 'u': + if (!process_escaped_unicode()) { + simdjson_consteval_error( + "Invalid unicode escape sequence in string"); + } + break; + default: + simdjson_consteval_error("Invalid escape character in string"); + } + } + continue; // continue to next iteration + } + // Handle escape sequences and UTF-8 validation. We do not process + // the escape sequences here, just validate them. + out += c; + if (static_cast(c) < 0x20) { + simdjson_consteval_error("Invalid control character in string"); + } + if (static_cast(c) >= 0x80) { + // We have a non-ASCII character inside a string + // We must validate it. + uint8_t first_byte = static_cast(c); + + if ((first_byte & 0b11100000) == 0b11000000) { + if (cursor == end) { + simdjson_consteval_error("Truncated UTF-8 sequence in string"); + } + + char second_byte = *cursor; + out += second_byte; + ++cursor; + if ((static_cast(second_byte) & 0b11000000) != 0b10000000) { + simdjson_consteval_error("Invalid UTF-8 continuation byte in string"); + } + // range check + uint32_t code_point = (first_byte & 0b00011111) << 6 | + (static_cast(second_byte) & 0b00111111); + if ((code_point < 0x80) || (0x7ff < code_point)) { + simdjson_consteval_error("Invalid UTF-8 code point in string"); + } + } else if ((first_byte & 0b11110000) == 0b11100000) { + if (cursor == end) { + simdjson_consteval_error("Truncated UTF-8 sequence in string"); + } + char second_byte = *cursor; + ++cursor; + out += second_byte; + if ((static_cast(second_byte) & 0b11000000) != 0b10000000) { + simdjson_consteval_error("Invalid UTF-8 continuation byte in string"); + } + if (cursor == end) { + simdjson_consteval_error("Truncated UTF-8 sequence in string"); + } + char third_byte = *cursor; + ++cursor; + out += third_byte; + if ((static_cast(third_byte) & 0b11000000) != 0b10000000) { + simdjson_consteval_error("Invalid UTF-8 continuation byte in string"); + } + // range check + uint32_t code_point = (first_byte & 0b00001111) << 12 | + (static_cast(second_byte) & 0b00111111) + << 6 | + (static_cast(third_byte) & 0b00111111); + if ((code_point < 0x800) || (0xffff < code_point) || + (0xd7ff < code_point && code_point < 0xe000)) { + simdjson_consteval_error("Invalid UTF-8 code point in string"); + } + } else if ((first_byte & 0b11111000) == 0b11110000) { // 0b11110000 + if (cursor == end) { + simdjson_consteval_error("Truncated UTF-8 sequence in string"); + } + char second_byte = *cursor; + ++cursor; + out += second_byte; + if (cursor == end) { + simdjson_consteval_error("Truncated UTF-8 sequence in string"); + } + char third_byte = *cursor; + ++cursor; + out += third_byte; + if (cursor == end) { + simdjson_consteval_error("Truncated UTF-8 sequence in string"); + } + char fourth_byte = *cursor; + ++cursor; + out += fourth_byte; + if ((static_cast(second_byte) & 0b11000000) != 0b10000000) { + simdjson_consteval_error("Invalid UTF-8 continuation byte in string"); + } + if ((static_cast(third_byte) & 0b11000000) != 0b10000000) { + simdjson_consteval_error("Invalid UTF-8 continuation byte in string"); + } + if ((static_cast(fourth_byte) & 0b11000000) != 0b10000000) { + simdjson_consteval_error("Invalid UTF-8 continuation byte in string"); + } + // range check + uint32_t code_point = + (first_byte & 0b00000111) << 18 | + (static_cast(second_byte) & 0b00111111) << 12 | + (static_cast(third_byte) & 0b00111111) << 6 | + (static_cast(fourth_byte) & 0b00111111); + if (code_point <= 0xffff || 0x10ffff < code_point) { + simdjson_consteval_error("Invalid UTF-8 code point in string"); + } + } else { + // we have a continuation + simdjson_consteval_error("Invalid UTF-8 continuation byte in string"); + } + continue; + } + } + if (cursor == end) { + simdjson_consteval_error("Unterminated string"); + } + if (*cursor != '"') { + simdjson_consteval_error("Internal error: expected closing quote"); + } + cursor++; // consume the closing quote + // We get here if and only if we have seen the closing quote. + + return {out, size_t(cursor - json.begin())}; +} + +//////////////////////////////////////////////////// +/// ARRAY PARSING +//////////////////////////////////////////////////// + +// Parses a JSON array and returns a std::meta::info representing the array as +// well as the number of characters consumed. +consteval std::pair +parse_json_array_impl(const std::string_view json) { + size_t consumed = 0; + auto cursor = json.begin(); + auto end = json.end(); + auto is_whitespace = [](char c) { + return c == ' ' || c == '\n' || c == '\t' || c == '\r'; + }; + + auto skip_whitespace = [&]() -> void { + while (cursor != end && is_whitespace(*cursor)) + cursor++; + }; + + auto expect_consume = [&] [[nodiscard]] (char c) -> bool { + skip_whitespace(); + if (cursor == end || *(cursor++) != c) { + return false; + }; + return true; + }; + + if (!expect_consume('[')) { + simdjson_consteval_error("Expected '['"); + } + + std::vector values = {^^void}; + skip_whitespace(); + if (cursor != end && *cursor == ']') { + if (!expect_consume(']')) { + simdjson_consteval_error("Expected ']'"); + } + // Empty array - use int as placeholder type since void doesn't work + auto array_type = std::meta::substitute( + ^^std::array, { + ^^int, std::meta::reflect_constant(0uz)}); + values[0] = array_type; + consumed = size_t(cursor - json.begin()); + if (json[consumed - 1] != ']') { + simdjson_consteval_error("Expected ']'"); + } + return {std::meta::substitute(^^construct_from, values), consumed}; + } + while (cursor != end && *cursor != ']') { + char c = *cursor; + switch (c) { + case '{': { + std::string_view value(cursor, end); + auto [parsed, object_size] = parse_json_object_impl(value); + if (*(cursor + object_size - 1) != '}') { + simdjson_consteval_error("Expected '}'"); + } + values.push_back(parsed); + cursor += object_size; + break; + } + case '[': { + std::string_view value(cursor, end); + auto [parsed, array_size] = parse_json_array_impl(value); + if (*(cursor + array_size - 1) != ']') { + simdjson_consteval_error("Expected ']'"); + } + values.push_back(parsed); + cursor += array_size; + break; + } + case '"': { + auto res = parse_string(std::string_view(cursor, end)); + cursor += res.second; + for (char ch : res.first) { + if (ch == '\0') { + simdjson_consteval_error( + "Field string values cannot contain embedded nulls"); + } + } + values.push_back(std::meta::reflect_constant_string(res.first)); + break; + } + case 't': { + if (end - cursor < 4 || std::string_view(cursor, 4) != "true") { + simdjson_consteval_error("Invalid value"); + } + cursor += 4; + values.push_back(std::meta::reflect_constant(true)); + break; + } + case 'f': { + if (end - cursor < 5 || std::string_view(cursor, 5) != "false") { + simdjson_consteval_error("Invalid value"); + } + cursor += 5; + values.push_back(std::meta::reflect_constant(false)); + break; + } + case 'n': { + if (end - cursor < 4 || std::string_view(cursor, 4) != "null") { + simdjson_consteval_error("Invalid value"); + } + cursor += 4; + values.push_back(std::meta::reflect_constant(nullptr)); + break; + } + // We deliberately do not include '+' here, as per JSON spec + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + std::string_view suffix = std::string_view(cursor, end); + std::variant out; + size_t r = parse_number(suffix, out); + cursor += r; + if (std::holds_alternative(out)) { + int64_t int_value = std::get(out); + values.push_back(std::meta::reflect_constant(int_value)); + } else if (std::holds_alternative(out)) { + uint64_t uint_value = std::get(out); + values.push_back(std::meta::reflect_constant(uint_value)); + } else { + double float_value = std::get(out); + values.push_back(std::meta::reflect_constant(float_value)); + } + break; + } + default: + simdjson_consteval_error("Invalid character starting value"); + } + skip_whitespace(); + if (cursor != end && *cursor == ',') { + ++cursor; + skip_whitespace(); + } + } + + if (!expect_consume(']')) { + simdjson_consteval_error("Expected ']'"); + } + std::size_t count = values.size() - 1; + // We assume all elements have the same type as the first element. + // However, if the array is heterogeneous, we should use std::variant. + auto array_type = std::meta::substitute( + ^^std::array, + { + std::meta::type_of(values[1]), std::meta::reflect_constant(count)}); + + // Create array instance with values + values[0] = array_type; + consumed = size_t(cursor - json.begin()); + if (json[consumed - 1] != ']') { + simdjson_consteval_error("Expected ']'"); + } + return {std::meta::substitute(^^construct_from, values), consumed}; +} + +//////////////////////////////////////////////////// +/// OBJECT PARSING +//////////////////////////////////////////////////// + +// Parses a JSON object and returns a std::meta::info representing the object +// type as well as the number of characters consumed. +consteval std::pair +parse_json_object_impl(std::string_view json) { + size_t consumed = 0; + auto cursor = json.begin(); + auto end = json.end(); + + auto skip_whitespace = [&]() -> void { + while (cursor != end && is_whitespace(*cursor)) + cursor++; + }; + + auto expect_consume = [&] [[nodiscard]] (char c) -> bool { + skip_whitespace(); + if (cursor == end || *(cursor++) != c) { + return false; + }; + return true; + }; + + if (!expect_consume('{')) { + simdjson_consteval_error("Expected '{'"); + } + + std::vector members; + std::vector values = {^^void}; + + while (cursor != end && *cursor != '}') { + skip_whitespace(); + // Not all strings can be identifiers, but in JSON field names can be any + // string. Thus we may have a problem if the field name contains characters + // not allowed in identifiers. Let the standard library handle that case. + auto field = parse_string(std::string_view(cursor, end)); + std::string field_name = field.first; + cursor += field.second; + if (!expect_consume(':')) { + simdjson_consteval_error("Expected ':'"); + } + skip_whitespace(); + if (cursor == end) { + simdjson_consteval_error("Expected value after colon"); + } + char c = *cursor; + switch (c) { + case '{': { + std::string_view value(cursor, end); + auto [parsed, object_size] = parse_json_object_impl(value); + if (*(cursor + object_size - 1) != '}') { + simdjson_consteval_error("Expected '}'"); + } + cursor += object_size; + auto dms = std::meta::data_member_spec(std::meta::type_of(parsed), + {.name = field_name}); + members.push_back(std::meta::reflect_constant(dms)); + values.push_back(parsed); + + break; + } + case '[': { + std::string_view value(cursor, end); + auto [parsed, array_size] = parse_json_array_impl(value); + auto dms = std::meta::data_member_spec(std::meta::type_of(parsed), + {.name = field_name}); + members.push_back(std::meta::reflect_constant(dms)); + values.push_back(parsed); + if (*(cursor + array_size - 1) != ']') { + simdjson_consteval_error("Expected ']'"); + } + cursor += array_size; + + break; + } + case '"': { + auto res = parse_string(std::string_view(cursor, end)); + std::string_view value = res.first; + cursor += res.second; + for (char ch : value) { + if (ch == '\0') { + simdjson_consteval_error( + "Field string values cannot contain embedded nulls"); + } + } + auto dms = + std::meta::data_member_spec(^^const char *, { + .name = field_name}); + members.push_back(std::meta::reflect_constant(dms)); + values.push_back(std::meta::reflect_constant_string(value)); + break; + } + case 't': { + if (end - cursor < 4 || std::string_view(cursor, 4) != "true") { + simdjson_consteval_error("Invalid value"); + } + cursor += 4; + + auto dms = std::meta::data_member_spec(^^bool, { + .name = field_name}); + members.push_back(std::meta::reflect_constant(dms)); + values.push_back(std::meta::reflect_constant(true)); + break; + } + case 'f': { + if (end - cursor < 5 || std::string_view(cursor, 5) != "false") { + simdjson_consteval_error("Invalid value"); + } + cursor += 5; + + auto dms = std::meta::data_member_spec(^^bool, { + .name = field_name}); + members.push_back(std::meta::reflect_constant(dms)); + values.push_back(std::meta::reflect_constant(false)); + break; + } + case 'n': { + if (end - cursor < 4 || std::string_view(cursor, 4) != "null") { + simdjson_consteval_error("Invalid value"); + } + cursor += 4; + + auto dms = std::meta::data_member_spec(^^std::nullptr_t, + { + .name = field_name}); + members.push_back(std::meta::reflect_constant(dms)); + values.push_back(std::meta::reflect_constant(nullptr)); + break; + } + // We deliberately do not include '+' here, as per JSON spec + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + std::string_view suffix = std::string_view(cursor, end); + std::variant out; + size_t r = parse_number(suffix, out); + cursor += r; + if (std::holds_alternative(out)) { + int64_t int_value = std::get(out); + auto dms = + std::meta::data_member_spec(^^int64_t, { + .name = field_name}); + members.push_back(std::meta::reflect_constant(dms)); + values.push_back(std::meta::reflect_constant(int_value)); + } else if (std::holds_alternative(out)) { + uint64_t uint_value = std::get(out); + auto dms = + std::meta::data_member_spec(^^uint64_t, { + .name = field_name}); + members.push_back(std::meta::reflect_constant(dms)); + values.push_back(std::meta::reflect_constant(uint_value)); + } else { + double float_value = std::get(out); + auto dms = + std::meta::data_member_spec(^^double, { + .name = field_name}); + members.push_back(std::meta::reflect_constant(dms)); + values.push_back(std::meta::reflect_constant(float_value)); + } + break; + } + default: + simdjson_consteval_error("Invalid character starting value"); + } + skip_whitespace(); + if (cursor == end) { + simdjson_consteval_error("Expected '}' or ','"); + } + if (*cursor == ',') { + ++cursor; + skip_whitespace(); + } else if (*cursor != '}') { + simdjson_consteval_error("Expected '}'"); + } + } + + if (!expect_consume('}')) { + simdjson_consteval_error("Expected '}'"); + } + values[0] = std::meta::substitute(^^class_type, members); + consumed = size_t(cursor - json.begin()); + if (json[consumed - 1] != '}') { + simdjson_consteval_error("Expected '}'"); + } + return {std::meta::substitute(^^construct_from, values), consumed}; +} + +/** + * Our public function to parse JSON at compile time. + * @brief Compile-time JSON parser. This function parses the provided JSON + * string at compile time and returns a custom struct type representing the JSON + * object. + */ +template consteval auto parse_json() { + constexpr std::string_view json = trim_whitespace(json_str.view()); + static_assert(!json.empty(), "JSON string cannot be empty"); + /*static_assert(json.front() != '{' && json.front() != '[', + "Only JSON objects and arrays are supported at the top level, this " + "limitation will be lifted in the future.");*/ + + constexpr auto result = json.front() == '[' + ? parse_json_array_impl(json) + : parse_json_object_impl(json); + return [: result.first :]; + /* + if(json.front() == '[') { + return [:parse_json_array_impl(json).first:]; + } else if(json.front() == '{') { + // return [:parse_json_object_impl(json).first:]; + }*/ +} + +} // namespace compile_time +} // namespace simdjson + +#endif // SIMDJSON_STATIC_REFLECTION +#endif // SIMDJSON_GENERIC_COMPILE_TIME_JSON_INL_H +/* end file simdjson/compile_time_json-inl.h */ #endif // SIMDJSON_H /* end file simdjson.h */